cur/example.rkt
2015-01-19 20:20:25 -05:00

267 lines
7.6 KiB
Racket

#lang s-exp "redex-core.rkt"
;; Use racket libraries over your dependently typed code!?!?
;; TODO: actually, I'm not sure this should work quite as well as it
;; seems to with check-equal?
(require rackunit)
;; -------------------
;; Define inductive data
(data true : Type (T : true))
(data false : Type)
;; -------------------
;; Look, syntax extension!
(define-syntax (-> syn)
(syntax-case syn ()
[(_ t1 t2)
(with-syntax ([(x) (generate-temporaries '(1))])
#'(forall (x : t1) t2))]))
(data nat : Type
(z : nat)
(s : (-> nat nat)))
(define nat-is-now-a-type
(lambda (y : (-> nat nat))
(lambda (x : nat) (y x))))
;; -------------------
;; Lexical scoping! I can reuse the name nat!
(define nat-is-just-a-name (lambda (nat : Type) nat))
;; -------------------
;; Reuse some familiar syntax
(define y (lambda (x : true) x))
(define (y1 (x : true)) x)
(define (y2 (x1 : true) (x2 : true)) x1)
;; -------------------
;; Unit test dependently typed code!
(check-equal? (y2 T T) T)
;; -------------------
;; Write functions on inductive data
(define (add1 (n : nat)) (s n))
(check-equal? (add1 (s z)) (s (s z)))
(define (sub1 (n : nat))
(case n
[z z]
[s (lambda (x : nat) x)]))
(check-equal? (sub1 (s z)) z)
;; TODO: Plus require recursion and I don't have recursion!
(define plus
(fix (plus : (forall* (n1 : nat) (n2 : nat) nat))
(lambda (n1 : nat)
(lambda (n2 : nat)
(case n1
[z n2]
[s (λ (x : nat) (plus x (s n2)))])))))
(check-equal? (plus (s (s z)) (s (s z))) (s (s (s (s z)))))
;; -------------------
;; It's annoying to have to write things explicitly curried
;; Macros to the rescue!
(define-syntax forall*
(syntax-rules (:)
[(_ (a : t) (ar : tr) ... b)
(forall (a : t)
(forall* (ar : tr) ... b))]
[(_ b) b]))
(define-syntax lambda*
(syntax-rules (:)
[(_ (a : t) (ar : tr) ... b)
(lambda (a : t)
(lambda* (ar : tr) ... b))]
[(_ b) b]))
(data and : (forall* (A : Type) (B : Type) Type)
(conj : (forall* (A : Type) (B : Type)
(x : A) (y : B) (and A B))))
;; -------------------
;; Prove interesting theorems!
(define (thm:and-is-symmetric
(x : (forall* (P : Type) (Q : Type)
;; TODO: Can't use -> for the final clause because generated
;; name has to match name in proof.
(ab : (and P Q))
(and P Q))))
T)
(define proof:and-is-symmetric
(lambda* (P : Type) (Q : Type) (ab : (and P Q))
(case ab
(conj (lambda* (P : Type) (Q : Type) (x : P) (y : Q) (conj Q P y x))))))
(check-equal? (thm:and-is-symmetric proof:and-is-symmetric) T)
;; -------------------
;; Ugh, why did the language implementer make the syntax for case so stupid?
;; I wish I could fix that ... Oh I can.
(begin-for-syntax
(define (rewrite-clause clause)
(syntax-case clause (:)
[((con (a : A) ...) body) #'(con (lambda* (a : A) ... body))]
[(e body) #'(e body)])))
(define-syntax (case* syn)
(syntax-case syn ()
[(_ e clause* ...)
#`(case e #,@(map rewrite-clause (syntax->list #'(clause* ...))))]))
(define proof:and-is-symmetric^
(lambda* (S : Type) (R : Type) (ab : (and S R))
(case* ab
[(conj (S : Type) (R : Type) (s : S) (r : R))
(conj R S r s)])))
(check-equal? (thm:and-is-symmetric proof:and-is-symmetric^) T)
;; -------------------
;; Gee, I wish there was a special syntax for theorems and proofs so I could think of
;; them as seperate from types and programs.
(define-syntax-rule (define-theorem name prop)
(define name prop))
(define-syntax-rule (qed thm pf)
(check-equal? T ((lambda (x : thm) T) pf)))
(define-theorem thm:and-is-symmetric^^
(forall* (P : Type) (Q : Type) (ab : (and P Q)) (and Q P)))
(qed thm:and-is-symmetric^^ proof:and-is-symmetric)
(define-theorem thm:proj1
(forall* (A : Type) (B : Type) (c : (and A B)) A))
(define proof:proj1
(lambda* (A : Type) (B : Type) (c : (and A B))
(case c (conj (lambda* (A : Type) (B : Type) (a : A) (b : B) a)))))
(qed thm:proj1 proof:proj1)
(define-theorem thm:proj2
(forall* (A : Type) (B : Type) (c : (and A B)) B))
(define proof:proj2
(lambda* (A : Type) (B : Type) (c : (and A B))
(case c (conj (lambda* (A : Type) (B : Type) (a : A) (b : B) b)))))
(qed thm:proj2 proof:proj2)
;; -------------------
;; Gee, I wish I had special syntax for defining types like I do for
;; defining functions.
(define-syntax-rule (define-type (name (a : t) ...) body)
(define name (forall* (a : t) ... body)))
(define-type (not (A : Type)) (-> A false))
#|
;;TODO: Can't pattern match on a inductive with 0 constructors yet.
(define-theorem thm:absurd
(forall* (P : Type) (A : Type) (a : A) (~a : (not A)) P))
(qed thm:absurd (lambda* (P : Type) (A : Type) (a : A) (~a : (not A))
(case (~a a))))
(define-theorem thm:absurdidty
(forall* (P : Type) (A : Type) (a*nota : (and A (not A))) P))
;; TODO: Not sure why this doesn't type-check.. probably not handling
;; inductive families correctly in (case ...)
(define (proof:absurdidty (P : Type) (A : Type) (a*nota : (and A (not A))))
((proof:proj2 A (not A) a*nota) (proof:proj1 A (not A) a*nota)))
|#
;; -------------------
;; Automate theorem proving! With real meta-programming, syntax is just data.
(define-syntax (inhabit-type syn)
(syntax-case syn (true false nat forall :)
[(_ true) #'T]
[(_ nat) #'z]
[(_ false)
(raise-syntax-error 'inhabit
"Actually, this type is unhabited" syn)]
;; TODO: We want all forall*s to be expanded by this point. Should
;; look into Racket macro magic to expand syn before matching on it.
[(_ (forall (x : t0) t1))
;; TODO: Should carry around assumptions to enable inhabit-type to use
;; them
#'(lambda (x : t0) (inhabit-type t1))]
[(_ t)
(raise-syntax-error 'inhabit
"Sorry, this type is too much for me" syn)]))
;; TODO: Would be great if meta-programs could reference things by name.
;; e.g. if I run (inhabit-type thm:true-is-proveable), it would first
;; lookup the syntax of the definition thm:true-is-proveable, then
;; run ... this would require some extra work in define, and a macro for
;; defining macros this way.
(define-theorem thm:true-is-proveable true)
(qed thm:true-is-proveable (inhabit-type true))
(define-theorem thm:anything-implies-true (forall (P : Type) true))
(qed thm:true-is-proveable (inhabit-type (forall (P : Type) true)))
#;(define is-this-inhabited? (inhabit-type false))
;; -------------------
;; Unit test your theorems before proving them!
(define-syntax ->*
(syntax-rules ()
[(->* a) a]
[(->* a a* ...)
(-> a (->* a* ...))]))
(define-theorem thm:true?
(forall* (A : Type) (B : Type) (P : Type)
;; If A implies P and (and A B) then P
(->* (-> A P) (and A B) P)))
(qed (thm:true? true true true)
;; TODO: inhabit-type ought to be able to reduce (type-thm:true? true true true)
;; but can't. Maybe instead there should be a reduce tactic/macro.
(inhabit-type (forall (a : (-> true true))
(forall (f : (and true true))
true))))
;; -------------------
;; Okay but what about *real* proofs, like formalizing STLC, complete
;; with fancy syntax?
(data type : Type
(Unit : type)
(Pair : (->* type type type))
(Fun : (->* type type type)))
(data var : Type
(v : (-> nat var)))
(data term : Type
(tvar : (-> var term))
(unitv : term)
(pair : (->* term term term))
(lam : (->* var type term term))
(prj : (->* nat term term))
(app : (->* term term term)))
(define-syntax (λ syn)
(syntax-case syn (:)
[(_ (x : t) e)
#'(lam (v x) t e)]))
(check-equal? (lam (v z) Unit unitv) (λ (z : Unit) unitv))