Compare commits

..

7 Commits

Author SHA1 Message Date
William J. Bowman
302d8014fa
Added TODO; Δ depends on order, but dict unordered 2016-01-11 19:25:31 -05:00
William J. Bowman
97a11ea253
Ported redex-core tests to hybrid-core & tested
* Ported the redex-core test-suite to hybrid-core. This test suite badly
  in need of abstraction.
* Tested and fixed the hybrid core; now runs
2016-01-11 19:04:36 -05:00
William J. Bowman
b318617f0e
Set hybrid-core to default; converted Δ to dict
* Hybrid core now used by default in this branch.
* Converted Δ to a Racket dictionary, started converting Δ abstractions
  to use dictionary.
2016-01-11 17:34:57 -05:00
William J. Bowman
c99b44bc09
Removed tests from hybrid-core
Tests are now in cur-test, so removed from hybrid-core
2016-01-11 17:34:57 -05:00
William J. Bowman
23d0cf3e6f
bla 2016-01-11 17:34:57 -05:00
William J. Bowman
c8f9f1c867
Added a TODO 2016-01-11 17:34:57 -05:00
William J. Bowman
0fd59566da
Started hybrid core 2016-01-11 17:34:57 -05:00
40 changed files with 3039 additions and 1707 deletions

View File

@ -45,10 +45,6 @@ Try it out: open up DrRacket and put the following in the definition area:
(if true
false
true)
(: + (-> Nat Nat Nat))
(define + plus)
(+ z (s z))
```
Try entering the following in the interaction area:
@ -56,23 +52,6 @@ Try entering the following in the interaction area:
(sub1 (s (s z)))
```
Don't like parenthesis? Use Cur with sweet-expressions:
```racket
#lang sweet-exp cur
require
cur/stdlib/sugar
cur/stdlib/bool
cur/stdlib/nat
if true
false
true
define + plus
{z + s(z)}
```
See the docs: `raco docs cur`.
Going further

View File

@ -17,7 +17,7 @@ Edwin C. Brady.
@(define curnel-eval (curnel-sandbox "(require cur/stdlib/nat cur/stdlib/bool cur/stdlib/prop)"))
@defform*[((Type n)
Type)]{
Type)]{
Define the universe of types at level @racket[n], where @racket[n] is any natural number.
@racket[Type] is a synonym for @racket[(Type 0)]. Cur is impredicative
in @racket[(Type 0)], although this is likely to change to a more
@ -33,44 +33,43 @@ restricted impredicative universe.
Type]
}
@defform[(λ (id : type-expr) body-expr)]{
Produces a single-arity procedure, binding the identifier @racket[id] of type
@racket[type-expr] in @racket[body-expr] and in the type of @racket[body-expr].
Both @racket[type-expr] and @racket[body-expr] can contain non-curnel forms,
such as macros.
@defform*[((lambda (id : type-expr) body-expr)
(λ (id : type-expr) body-expr))]{
Produces a single arity procedure, binding the identifier @racket[id] of type @racket[type-expr] in @racket[body-expr] and in the type of
@racket[body-expr].
Both @racket[type-expr] and @racket[body-expr] can contain non-curnel forms, such as macros.
Currently, Cur will return the underlying representation of a procedure when a
@racket[λ] is evaluated at the top-level.
Do not rely on this representation.
Currently, Cur will return the underlying representation of a procedure when a @racket[lambda] is
evaluated at the top-level. Do not rely on this representation.
@examples[#:eval curnel-eval
(λ (x : Type) x)]
(lambda (x : Type) x)]
@examples[#:eval curnel-eval
(λ (x : Type) (λ (y : x) y))]
(λ (x : Type) (lambda (y : x) y))]
@defform[(#%app procedure argument)]{
Applies the single-arity @racket[procedure] to @racket[argument].
Applies the single arity @racket[procedure] to @racket[argument].
}
@examples[#:eval curnel-eval
((λ (x : (Type 1)) x) Type)]
((lambda (x : (Type 1)) x) Type)]
@examples[#:eval curnel-eval
(#%app (λ (x : (Type 1)) x) Type)]
(#%app (lambda (x : (Type 1)) x) Type)]
}
@defform[(Π (id : type-expr) body-expr)]{
Produces a dependent function type, binding the identifier @racket[id] of type
@racket[type-expr] in @racket[body-expr].
@defform*[((forall (id : type-expr) body-expr)
(∀ (id : type-expr) body-expr))]{
Produces a dependent function type, binding the identifier @racket[id] of type @racket[type-expr] in @racket[body-expr].
@examples[#:eval curnel-eval
(Π (x : Type) Type)]
(forall (x : Type) Type)]
@examples[#:eval curnel-eval
(λ (x : (Π (x : (Type 1)) Type))
(lambda (x : (forall (x : (Type 1)) Type))
(x Type))]
}
@ -84,35 +83,32 @@ For instance, Cur does not currently perform strict positivity checking.
(data Bool : Type
(true : Bool)
(false : Bool))
((λ (x : Bool) x) true)
((lambda (x : Bool) x) true)
(data False : Type)
(data And : (Π (A : Type) (Π (B : Type) Type))
(conj : (Π (A : Type) (Π (B : Type) (Π (a : A) (Π (b : B) ((And A) B)))))))
(data And : (forall (A : Type) (forall (B : Type) Type))
(conj : (forall (A : Type) (forall (B : Type) (forall (a : A) (forall (b : B) ((And A) B)))))))
((((conj Bool) Bool) true) false)]
}
@defform[(elim inductive-type motive (index ...) (method ...) disc)]{
Fold over the term @racket[disc] of the inductively defined type @racket[inductive-type].
The @racket[motive] is a function that expects the indices of the inductive
type and a term of the inductive type and produces the type that this
fold returns.
The type of @racket[disc] is @racket[(inductive-type index ...)].
@racket[elim] takes one method for each constructor of @racket[inductive-type].
Each @racket[method] expects the arguments for its corresponding constructor,
and the inductive hypotheses generated by recursively eliminating all recursive
arguments of the constructor.
@defform[(elim type motive-universe)]{
Returns the inductive eliminator for @racket[type] where the @racket[motive-universe] is the universe
of the motive.
The eliminator expects the next argument to be the motive, the next @racket[N] arguments to be the methods for
each of the @racket[N] constructors of the inductive type @racket[type], the next @racket[P] arguments
to be the parameters @racket[p_0 ... p_P] of the inductive @racket[type], and the final argument to be the term to
eliminate of type @racket[(type p_0 ... p_P)].
The following example runs @racket[(sub1 (s z))].
@examples[#:eval curnel-eval
(data Nat : Type
(z : Nat)
(s : (Π (n : Nat) Nat)))
(elim Nat (λ (x : Nat) Nat)
()
(z
(λ (n : Nat) (λ (IH : Nat) n)))
(s z))]
(s : (forall (n : Nat) Nat)))
(((((elim Nat Type)
(lambda (x : Nat) Nat))
z)
(lambda (n : Nat) (lambda (IH : Nat) n)))
(s z))]
}
@defform[(define id expr)]{
@ -121,13 +117,11 @@ Binds @racket[id] to the result of @racket[expr].
@examples[#:eval curnel-eval
(data Nat : Type
(z : Nat)
(s : (Π (n : Nat) Nat)))
(define sub1 (λ (n : Nat)
(elim Nat (λ (x : Nat) Nat)
()
(z
(λ (n : Nat) (λ (IH : Nat) n)))
n)))
(s : (forall (n : Nat) Nat)))
(define sub1 (lambda (n : Nat)
(((((elim Nat Type) (lambda (x : Nat) Nat))
z)
(lambda (n : Nat) (lambda (IH : Nat) n))) n)))
(sub1 (s (s z)))
(sub1 (s z))
(sub1 z)]

View File

@ -14,11 +14,11 @@ the language.
maybe-vars
maybe-output-coq
maybe-output-latex
(nt-name (nt-metavar ...) maybe-bnfdef non-terminal-def ...) ...)
(nt-name (nt-metavars) maybe-bnfdef constructors ...) ...)
#:grammar
[(maybe-vars
(code:line)
(code:line #:vars (nt-metavar ...)))
(code:line #:vars (nt-metavars ...)))
(maybe-output-coq
(code:line)
(code:line #:output-coq coq-filename))
@ -27,51 +27,26 @@ the language.
(code:line #:output-latex latex-filename))
(maybe-bnfdef
(code:line)
(code:line ::=))
(non-terminal-def
nt-metavar
(code:line terminal)
(code:line (terminal terminal-args ...)))
(terminal-args
nt-metavar
(code:line literal)
(code:line ())
(code:line (#:bind nt-metavar . terminal-args))
(code:line (terminal-args terminal-args ...)))]]{
(code:line ::=))]]{
Defines a new language by generating inductive definitions for each
non-terminal @racket[nt-name], whose syntax is generated by
@racket[non-terminal-def].
Each @racket[non-terminal-def] must either be a reference to a
previously defined non-terminal using a @racket[nt-metavar], a
@racket[terminal] (an identifier), or a @racket[terminal] applied to
some @racket[terminal-args].
The @racket[terminal-args] are a limited grammar of s-expressions,
which can reference previously defined @racket[nt-metavar]s to be
treated as arguments, literal symbols to be treated as syntax, binding
declarations, or a nested @racket[terminal-arg].
nonterminal @racket[nt-name], whose constructors are generated by
@racket[constructors]. The constructors must either with a tag that can
be used to name the constructor, or be another meta-variable.
The meta-variables @racket[nt-metavars] are replaced by the corresponding
inductive types in @racket[constructors].
The name of each inductive datatype is generated by
@racket[(format-id "~a-~a" name nt-name)].
The inductive definitions are generated by generating a type for each
@racket[nt-name] whose name @racket[nt-type] is generated by
@racket[(format-id name "~a-~a" name nt-name)] and whose constructors
are generated by each @racket[non-terminal-def].
For @racket[terminal]s, the constructor is a base constructor whose
name is generated by @racket[(format-id name "~a-~a" name terminal)].
For @racket[nt-metavar]s, the constructor is a conversion constructor
whose name is generated by @racket[(format-id name "~a->~a" nt-type
nt-metavar-type)] where @racket[nt-metavar-type] is the name of the
type generated for the nonterminal to which @racket[nt-metavars] refers.
For @racket[(terminal terminal-args ...)], the constructor is a
function that expects term of of the types generated by
@racket[terminal-args ...].
Later nonterminals can refer to prior nonterminals, but they cannot be
mutually inductive due to limitations in Cur. When nonterminals appear
in @racket[constructors], a constructor is defined that coerces one
nonterminal to another.
If @racket[#:vars] is given, it should be a list of meta-variables that
representing variables in the language.
These variables are represented as De Bruijn indices, and uses of
variables in the syntax are treated as type @racket[Nat].
Binding positions in the syntax, represented by @racket[#:bind
nt-metavar], are erased in converting to De Bruijn indices, although
this may change if the representation of variables used by
@racket[define-language] changes.
representing variables in the language. These meta-variables should only
appear in binding positions in @racket[constructors]. These variables
are represented as De Bruijn indexes, and Olly provides some functions
for working with De Bruijn indexes.
If @racket[#:output-coq] is given, it should be a string representing a
filename. The form @racket[define-language] will output Coq versions of
@ -91,8 +66,8 @@ Example:
#:output-latex "stlc.tex"
(val (v) ::= true false unit)
(type (A B) ::= boolty unitty (-> A B) (* A A))
(term (e) ::= x v (app e e) (lambda (#:bind x : A) e) (cons e e)
(let (#:bind x #:bind x) = e in e)))
(term (e) ::= x v (app e e) (lambda (x : A) e) (cons e e)
(let (x x) = e in e)))
]
This example is equivalent to
@ -110,17 +85,20 @@ This example is equivalent to
(stlc-* : (forall (x : stlc-type) (forall (y : stlc-type) stlc-type))))
(data stlc-term : Type
(Var->-stlc-term : (forall (x : Nat) stlc-term))
(stlc-val->-stlc-term : (forall (x : stlc-val) stlc-term))
(stlc-term-lambda : (forall (y : stlc-type)
(stlc-var-->-stlc-term : (forall (x : Var) stlc-term))
(stlc-val-->-stlc-term : (forall (x : stlc-val) stlc-term))
(stlc-term-lambda : (forall (x : Var)
(forall (y : stlc-type)
(forall (z : stlc-term)
stlc-term)))
stlc-term))))
(stlc-term-cons : (forall (x : stlc-term) (forall (y : stlc-term) stlc-term)))
(stlc-term-let : (forall (e1 : stlc-term)
(forall (e2 : stlc-term)
stlc-term))))]
(stlc-term-let : (forall (x : Var)
(forall (y : Var)
(forall (e1 : stlc-term)
(forall (e2 : stlc-term)
stlc-term))))))]
@margin-note{This example is taken from @racketmodname[cur/tests/stlc]}
@margin-note{This example is taken from @racketmodname[cur/examples/stlc]}
}
@defform[(define-relation (name args ...)
@ -148,20 +126,20 @@ explain the syntax in detail, here is an example:
#:output-latex "stlc.tex"
[(g : Gamma)
------------------------ T-Unit
(has-type g (stlc-val->stlc-term stlc-unit) stlc-unitty)]
(has-type g (stlc-val-->-stlc-term stlc-unit) stlc-unitty)]
[(g : Gamma)
------------------------ T-True
(has-type g (stlc-val->stlc-term stlc-true) stlc-boolty)]
(has-type g (stlc-val-->-stlc-term stlc-true) stlc-boolty)]
[(g : Gamma)
------------------------ T-False
(has-type g (stlc-val->stlc-term stlc-false) stlc-boolty)]
(has-type g (stlc-val-->-stlc-term stlc-false) stlc-boolty)]
[(g : Gamma) (x : Nat) (t : stlc-type)
[(g : Gamma) (x : Var) (t : stlc-type)
(== (Maybe stlc-type) (lookup-gamma g x) (some stlc-type t))
------------------------ T-Var
(has-type g (Var->stlc-term x) t)]
(has-type g (Var-->-stlc-term x) t)]
[(g : Gamma) (e1 : stlc-term) (e2 : stlc-term)
(t1 : stlc-type) (t2 : stlc-type)
@ -173,15 +151,16 @@ explain the syntax in detail, here is an example:
[(g : Gamma) (e1 : stlc-term) (e2 : stlc-term)
(t1 : stlc-type) (t2 : stlc-type)
(t : stlc-type)
(x : Var) (y : Var)
(has-type g e1 (stlc-* t1 t2))
(has-type (extend-gamma (extend-gamma g t1) t2) e2 t)
(has-type (extend-gamma (extend-gamma g x t1) y t2) e2 t)
---------------------- T-Let
(has-type g (stlc-let e1 e2) t)]
(has-type g (stlc-let x y e1 e2) t)]
[(g : Gamma) (e1 : stlc-term) (t1 : stlc-type) (t2 : stlc-type)
(has-type (extend-gamma g t1) e1 t2)
[(g : Gamma) (e1 : stlc-term) (t1 : stlc-type) (t2 : stlc-type) (x : Var)
(has-type (extend-gamma g x t1) e1 t2)
---------------------- T-Fun
(has-type g (stlc-lambda t1 e1) (stlc--> t1 t2))]
(has-type g (stlc-lambda x t1 e1) (stlc--> t1 t2))]
[(g : Gamma) (e1 : stlc-term) (e2 : stlc-term)
(t1 : stlc-type) (t2 : stlc-type)
@ -200,11 +179,22 @@ This example is equivalent to:
(T-Unit : (forall (g : Gamma)
(has-type
g
(stlc-val->stlc-term stlc-unit)
(stlc-val-->-stlc-term stlc-unit)
stlc-unitty)))
....)]
}
@deftogether[(@defthing[Var Type]
@defthing[avar (forall (x : Nat) Var)])]{
The type of a De Bruijn variable.
}
@defproc[(var-equal? [v1 Var] [v2 Var])
Bool]{
A Cur procedure; returns @racket[true] if @racket[v1] and @racket[v2]
represent the same variable.
}
@todo{Need a Scribble library for defining Cur/Racket things in the same
library in a nice way.}

View File

@ -25,61 +25,61 @@ phase 1 in Cur.}
@examples[
(eval:alts (define-syntax-rule (computed-type _) Type) (void))
(eval:alts (cur-expand #'(λ (x : (computed-type bla)) x))
(eval:result @racket[#'(λ (x : Type) x)] "" ""))
(eval:alts (cur-expand #'(lambda (x : (computed-type bla)) x))
(eval:result @racket[#'(lambda (x : Type) x)] "" ""))
]
}
@defproc[(cur-type-infer [syn syntax?])
@defproc[(type-infer/syn [syn syntax?])
(or/c syntax? #f)]{
Returns the type of the Cur term @racket[syn], or @racket[#f] if no type could be inferred.
@examples[
(eval:alts (cur-type-infer #'(λ (x : Type) x))
(eval:result @racket[#'(Π (x : (Type 0)) (Type 0))] "" ""))
(eval:alts (cur-type-infer #'Type)
(eval:alts (type-infer/syn #'(lambda (x : Type) x))
(eval:result @racket[#'(forall (x : (Type 0)) (Type 0))] "" ""))
(eval:alts (type-infer/syn #'Type)
(eval:result @racket[#'(Type 1)] "" ""))
]
}
@defproc[(cur-type-check? [syn syntax?])
@defproc[(type-check/syn? [syn syntax?])
boolean?]{
Returns @racket[#t] if the Cur term @racket[syn] is well-typed, or @racket[#f] otherwise.
@examples[
(eval:alts (cur-type-check? #'(λ (x : Type) x))
(eval:alts (type-check/syn? #'(lambda (x : Type) x))
(eval:result @racket[#t] "" ""))
(eval:alts (cur-type-check? #'Type)
(eval:alts (type-check/syn? #'Type)
(eval:result @racket[#t] "" ""))
(eval:alts (cur-type-check? #'x)
(eval:alts (type-check/syn? #'x)
(eval:result @racket[#f] "" ""))
]
}
@defproc[(cur-normalize [syn syntax?])
@defproc[(normalize/syn [syn syntax?])
syntax?]{
Runs the Cur term @racket[syn] to a value.
@examples[
(eval:alts (cur-normalize #'((λ (x : Type) x) Bool))
(eval:alts (normalize/syn #'((lambda (x : Type) x) Bool))
(eval:result @racket[#'Bool] "" ""))
(eval:alts (cur-normalize #'(sub1 (s (s z))))
(eval:alts (normalize/syn #'(sub1 (s (s z))))
(eval:result @racket[#'(s z)] "" ""))
]
}
@defproc[(cur-step [syn syntax?])
@defproc[(step/syn [syn syntax?])
syntax?]{
Runs the Cur term @racket[syn] for one step.
@examples[
(eval:alts (cur-step #'((λ (x : Type) x) Bool))
(eval:alts (step/syn #'((lambda (x : Type) x) Bool))
(eval:result @racket[#'Bool] "" ""))
(eval:alts (cur-step #'(sub1 (s (s z))))
(eval:result @racket[#'(elim Nat (λ (x2 : Nat) Nat)
()
(z (λ (x2 : Nat) (λ (ih-n2 : Nat) x2)))
(s (s z)))] "" ""))
(eval:alts (step/syn #'(sub1 (s (s z))))
(eval:result @racket[#'(((((elim Nat (Type 0))
(lambda (x2 : Nat) Nat)) z)
(lambda (x2 : Nat) (lambda (ih-n2 : Nat) x2)))
(s (s z)))] "" ""))
]
}
@ -90,11 +90,11 @@ equal modulo α and β-equivalence.
@examples[
(eval:alts (cur-equal? #'(λ (a : Type) a) #'(λ (b : Type) b))
(eval:alts (cur-equal? #'(lambda (a : Type) a) #'(lambda (b : Type) b))
(eval:result @racket[#t] "" ""))
(eval:alts (cur-equal? #'((λ (a : Type) a) Bool) #'Bool)
(eval:alts (cur-equal? #'((lambda (a : Type) a) Bool) #'Bool)
(eval:result @racket[#t] "" ""))
(eval:alts (cur-equal? #'(λ (a : Type) (sub1 (s z))) #'(λ (a : Type) z))
(eval:alts (cur-equal? #'(lambda (a : Type) (sub1 (s z))) #'(lambda (a : Type) z))
(eval:result @racket[#f] "" ""))
]
}
@ -106,7 +106,7 @@ Converts @racket[s] to a datum representation of the @tech{curnel form}, after e
@examples[
(eval:alts (cur->datum #'(λ (a : Type) a))
(eval:alts (cur-?datum #'(lambda (a : Type) a))
(eval:result @racket['(λ (a : (Unv 0) a))] "" ""))
]
}

View File

@ -7,10 +7,9 @@ Cur has a small standard library, primary for demonstration purposes.
@local-table-of-contents[]
@include-section{stdlib/tactics.scrbl}
@include-section{stdlib/sugar.scrbl}
@include-section{stdlib/bool.scrbl}
@include-section{stdlib/nat.scrbl}
@include-section{stdlib/maybe.scrbl}
@include-section{stdlib/list.scrbl}
@include-section{stdlib/typeclass.scrbl}
@include-section{stdlib/tactics.scrbl}

View File

@ -22,7 +22,7 @@ A syntactic form that expands to the inductive eliminator for @racket[Bool]. Thi
@examples[#:eval curnel-eval
(if true false true)
(elim Bool (λ (x : Bool) Bool) () (false true) true)]
(elim Bool Type (λ (x : Bool) Bool) false true true)]
}
@defproc[(not [x Bool])

View File

@ -1,21 +0,0 @@
#lang scribble/manual
@(require
"../defs.rkt"
scribble/eval)
@(define curnel-eval (curnel-sandbox "(require cur/stdlib/bool cur/stdlib/nat cur/stdlib/sugar cur/stdlib/list)"))
@title{List}
@defmodule[cur/stdlib/list]
This library defines the datatype @racket[List] and several functions on them.
@deftogether[(@defthing[List (-> Type Type)]
@defthing[nil (forall (A : Type) (List A))]
@defthing[cons (forall (A : Type) (a : A) (-> (List A) (List A)))])]{
The polymorphic list datatype.
}
@defproc[(list-ref [A Type] [ls (List A)] [n Nat]) (Maybe A)]{
Returns the @racket[n]th element of @racket[ls] in the @racket[Maybe] monad.
}

View File

@ -4,7 +4,7 @@
"../defs.rkt"
scribble/eval)
@(define curnel-eval (curnel-sandbox "(require cur/stdlib/nat cur/stdlib/bool cur/stdlib/sugar cur/stdlib/list)"))
@(define curnel-eval (curnel-sandbox "(require cur/stdlib/nat cur/stdlib/bool cur/stdlib/sugar)"))
@title{Sugar}
@ -18,38 +18,46 @@ that expands into the eliminator.
@defmodule[cur/stdlib/sugar]
This library defines various syntactic extensions making Cur easier to write than writing raw TT.
@defform*[((-> decl decl ... type)
(→ decl decl ... type)
(forall decl decl ... type)
(∀ decl decl ... type)
(Π decl decl ... type)
(Pi decl decl ... type))
#:grammar
[(decl
type
(code:line (identifier : type)))]]{
A multi-artiy function type that supports dependent and non-dependent type declarations and automatic currying.
We provide lots of names for this form, because there are lots of synonyms in the literature.
@defform*[((-> t1 t2)
(→ t1 t2))]{
A non-dependent function type Equivalent to @racket[(forall (_ : t1) t2)], where @racket[_] indicates an variable that is not used.
@examples[#:eval curnel-eval
(data And : (-> Type Type Type)
(conj : (-> (A : Type) (B : Type) A B ((And A) B))))
(data And : (-> Type (-> Type Type))
(conj : (forall (A : Type) (forall (B : Type) (-> A (-> B ((And A) B)))))))
((((conj Bool) Bool) true) false)]
}
@defform*[((->* t ...)
(→* t ...))]{
A non-dependent multi-arity function type that supports automatic currying.
@examples[#:eval curnel-eval
(data And : (forall Type Type Type)
(conj : (forall (A : Type) (B : Type) A B (And A B))))
(data And : (->* Type Type Type)
(conj : (forall (A : Type) (forall (B : Type) (->* A B ((And A) B))))))
((((conj Bool) Bool) true) false)]
}
@defform*[((forall* (a : t) ... type)
(∀* (a : t) ... type))]{
A multi-arity function type that supports automatic currying.
@examples[#:eval curnel-eval
(data And : (->* Type Type Type)
(conj : (forall* (A : Type) (B : Type)
(->* A B ((And A) B)))))
((((conj Bool) Bool) true) false)]
}
@defform*[((lambda (a : t) ... body)
(λ (a : t) ... body))]{
@defform*[((lambda* (a : t) ... body)
* (a : t) ... body))]{
Defines a multi-arity procedure that supports automatic currying.
@examples[#:eval curnel-eval
((lambda (x : Bool) (lambda (y : Bool) y)) true)
((lambda (x : Bool) (y : Bool) y) true)
((lambda* (x : Bool) (y : Bool) y) true)
]
}
@ -57,43 +65,37 @@ Defines a multi-arity procedure that supports automatic currying.
Defines multi-arity procedure application via automatic currying.
@examples[#:eval curnel-eval
(data And : (-> Type Type Type)
(conj : (-> (A : Type) (B : Type) A B ((And A) B))))
(data And : (->* Type Type Type)
(conj : (forall* (A : Type) (B : Type)
(->* A B ((And A) B)))))
(conj Bool Bool true false)]
}
@defform[(: name type)]{
Declare that the @emph{function} which will be defined as @racket[name] has type @racket[type].
Must precede the definition of @racket[name].
@racket[type] must expand to a function type of the form @racket[(Π (x : t1) t2)]
When used, this form allows omitting the annotations on arguments in the definition @racket[name]
@defform*[((define name body)
(define (name (x : t) ...) body))]{
Like the @racket[define] provided by @racketmodname[cur/curnel/redex-lang], but supports
defining curried functions via @racket[lambda*].
}
@defform*[((define name body)
(define (name x ...) body)
(define (name (x : t) ...) body))]{
Like the @racket[define] provided by @racketmodname[cur], but supports
defining curried functions via @racket[lambda].
The second form, @racket[(define (name x ...) body)], can only be used when
a @racket[(: name type)] form appears earlier in the module.
@defform[(elim type motive-result-type e ...)]{
Like the @racket[elim] provided by @racketmodname[cur/curnel/redex-lang], but supports
automatically curries the remaining arguments @racket[e ...].
@examples[#:eval curnel-eval
(: id (forall (A : Type) (a : A) A))
(define (id A a) a)]
(elim Bool Type (lambda (x : Bool) Bool)
false
true
true)]
}
@defform*[((define-type name type)
(define-type (name (a : t) ...) body))]{
Like @racket[define], but uses @racket[forall] instead of @racket[lambda].
Like @racket[define], but uses @racket[forall*] instead of @racket[lambda*].
}
@defform[(match e [maybe-in] [maybe-return] [pattern body] ...)
@defform[(match e [pattern body] ...)
#:grammar
[(maybe-in
(code:line #:in type))
(maybe-return
(code:line #:return type))
(pattern
[(pattern
constructor
(code:line (constructor (x : t) ...)))]]{
A pattern-matcher-like syntax for inductive elimination.
@ -106,9 +108,6 @@ Generates a call to the inductive eliminator for @racket[e].
Currently does not work on inductive type-families as types indices
are not inferred.
If @racket[#:in] is not specified, attempts to infer the type of @racket[e].
If @racket[#:return] is not specified, attempts to infer the return type of the @racket[match].
@examples[#:eval curnel-eval
(match z
[z true]
@ -117,31 +116,42 @@ If @racket[#:return] is not specified, attempts to infer the return type of the
@examples[#:eval curnel-eval
(match (s z)
#:in Nat
#:return Bool
[z true]
[(s (n : Nat))
(not (recur n))])]
@examples[#:eval curnel-eval
((match (nil Bool)
[(nil (A : Type))
(lambda (n : Nat)
(none A))]
[(cons (A : Type) (a : A) (rest : (List A)))
(lambda (n : Nat)
(match n
[z (some A a)]
[(s (n-1 : Nat))
((recur rest) n-1)]))])
z)]
}
@defform[(recur id)]{
A form valid only in the body of a @racket[match] clause.
Generates a reference to the induction hypothesis for @racket[x]
inferred by a @racket[match] clause.
A form valid only in the body of a @racket[match] clause. Generates a
reference to the induction hypothesis for @racket[x].
}
@defform[(case* type motive-result-type e (parameters ...) motive [pattern maybe-IH body] ...)
#:grammar
[(pattern
constructor
(code:line)
(code:line (constructor (x : t) ...)))
(maybe-IH
(code:line)
(code:line IH: ((x : t) ...)))]]{
A pattern-matcher-like syntax for inductive elimination that does not try to infer the type or motive.
Necessary for more advanced types, like @racket[And], because @racket[case] is not very smart.
@margin-note{Don't worry about all that output from requiring prop}
@examples[#:eval curnel-eval
(case* Nat Type z () (lambda (x : Nat) Bool)
[z true]
[(s (n : Nat))
IH: ((_ : Bool))
false])
(require cur/stdlib/prop)
(case* And Type (conj Bool Nat true z) (Bool Nat)
(lambda* (A : Type) (B : Type) (ab : (And A B)) A)
[(conj (A : Type) (B : Type) (a : A) (b : B))
IH: ()
a])]
}
@defform[(let (clause ...) body)
@ -172,9 +182,8 @@ Check that expression @racket[e] has type @racket[type], causing a type-error if
}
@defform[(run syn)]{
Like @racket[cur-normalize], but is a syntactic form to be used in surface syntax.
Allows a Cur term to be written by computing part of the term from
another Cur term.
Like @racket[normalize/syn], but is a syntactic form which allows a Cur term to be written by
computing part of the term from another Cur term.
@examples[#:eval curnel-eval
(lambda (x : (run (if true Bool Nat))) x)]
@ -182,7 +191,7 @@ another Cur term.
}
@defform[(step syn)]{
Like @racket[run], but uses @racket[cur-step] to evaluate only one step and prints intermediate
Like @racket[run], but uses @racket[step/syn] to evaluate only one step and prints intermediate
results before returning the result of evaluation.
@examples[#:eval curnel-eval

View File

@ -21,7 +21,7 @@ are @racket[t ...].
@examples[#:eval curnel-eval
(typeclass (Eqv (A : Type))
(equal? : (forall (a : A) (b : A) Bool)))]
(equal? : (forall* (a : A) (b : A) Bool)))]
}
@defform[(impl (class param) defs ...)]{

View File

@ -1,6 +1,6 @@
#lang info
(define collection 'multi)
(define deps '("base" "racket-doc"))
(define build-deps '("scribble-lib" ("cur-lib" #:version "0.4") "sandbox-lib"))
(define build-deps '("scribble-lib" ("cur-lib" #:version "0.2") "sandbox-lib"))
(define pkg-desc "Documentation for \"cur\".")
(define pkg-authors '(wilbowma))

View File

@ -5,7 +5,7 @@
racket/syntax
syntax/parse
(for-template
(only-in "curnel/redex-lang.rkt"
(only-in "curnel/hybrid-lang.rkt"
cur-expand)))
(provide cur-match)
@ -17,7 +17,7 @@
[pattern body] ...)])))
(require
(rename-in "curnel/redex-lang.rkt" [provide -provide])
(rename-in "curnel/hybrid-lang.rkt" [provide -provide])
(only-in racket/base eof)
(for-syntax 'extra)
'extra)
@ -27,5 +27,5 @@
(except-out
(all-from-out
'extra
"curnel/redex-lang.rkt")
"curnel/hybrid-lang.rkt")
-provide))

View File

@ -0,0 +1,648 @@
#lang racket/base
;; A mostly Redex core, with parts written in Racket for performance reasons
(require
racket/dict
racket/function
racket/list
redex/reduction-semantics)
(provide
(all-defined-out))
(set-cache-size! 10000)
(define-language base
(dict ::= any))
;; TODO: More abstractions for Redex dictionaries.
(define make-dict make-immutable-hash)
#| ttL is the core language of Cur. Very similar to TT (Idirs core) and Luo's UTT. Surface
| langauge should provide short-hand, such as -> for non-dependent function types, and type
| inference.
|#
(define-extended-language ttL base
(i j k ::= natural)
(U ::= (Unv i))
(t e ::= U (λ (x : t) e) x (Π (x : t) t) (e e) (elim D U))
(Δ ::= dict)
(D x c ::= variable-not-otherwise-mentioned)
#:binding-forms
(λ (x : t) e #:refers-to x)
(Π (x : t_0) t_1 #:refers-to x))
(define x? (redex-match? ttL x))
(define t? (redex-match? ttL t))
(define e? (redex-match? ttL e))
(define U? (redex-match? ttL U))
;; TODO: Constracts
;; An inductive-decl contains the type of the type being declared,
;; a t?, and a dictionary of constructor names (x?) mapped to their
;; types (t?), the original syntax/order of the constructor
;; declaration ((x : t) ...), and the list of constructors in the
;; original order (x ...)
(define-struct inductive-decl (type constr-dict constr-decl constr-ls) #:prefab)
;; A Δ is a dict mapping names x? to inductive-decl?
(define Δ? dict?)
(define make-empty-Δ make-dict)
(define empty-Δ? dict-empty?)
;;; ------------------------------------------------------------------------
;;; Universe typing
(define-judgment-form ttL
#:mode (unv-type I O)
#:contract (unv-type U U)
[(where i_1 ,(add1 (term i_0)))
-----------------
(unv-type (Unv i_0) (Unv i_1))])
;; Universe predicativity rules. Impredicative in (Unv 0)
(define-judgment-form ttL
#:mode (unv-pred I I O)
#:contract (unv-pred U U U)
[----------------
(unv-pred (Unv i) (Unv 0) (Unv 0))]
[(where i_3 ,(max (term i_1) (term i_2)))
----------------
(unv-pred (Unv i_1) (Unv i_2) (Unv i_3))])
(define-metafunction ttL
α-equivalent? : t t -> #t or #f
[(α-equivalent? t_0 t_1)
,(alpha-equivalent? ttL (term t_0) (term t_1))])
;; Replace x by t_1 in t_0
(define-metafunction ttL
subst : t x t -> t
[(subst t_0 x t_1)
(substitute t_0 x t_1)])
(define-metafunction ttL
subst-all : t (x ...) (e ...) -> t
[(subst-all t () ()) t]
[(subst-all t (x_0 x ...) (e_0 e ...))
(subst-all (subst t x_0 e_0) (x ...) (e ...))])
;;; ------------------------------------------------------------------------
;;; Primitive Operations on signatures Δ (those operations that do not require contexts)
;; TODO: Maybe shouldn't fall back, but maintains redex-core interface.
;; Get the type of x as declared in Δ, as either a constructor or an inductive type
(define-metafunction ttL
Δ-ref-type : Δ x -> t or #f
[(Δ-ref-type Δ x)
,(cond
[(dict-ref (term Δ) (term x) (thunk #f))
=> inductive-decl-type]
[else (term (Δ-ref-constructor-type Δ foo x))])])
;; Get the type of a constructor x in the inductive declaration Δ
;; TODO: Doesn't need x_D anymore
(define-metafunction ttL
Δ-ref-constructor-type : Δ x x -> t or #f
[(Δ-ref-constructor-type Δ x_D x)
,(cond
[(for/or ([(D idecl) (in-dict (term Δ))])
(let ([constr-dict (inductive-decl-constr-dict idecl)])
(and (dict-has-key? constr-dict (term x)) constr-dict)))
=>
(curryr dict-ref (term x))]
[else #f])])
;; Add an inductive declaration to Δ
(define-metafunction ttL
Δ-set : Δ x t ((x : t) ...) -> Δ
[(Δ-set Δ x t (name decl ((c : t_c) ...)))
,(dict-set
(term Δ)
(term x)
(inductive-decl
(term t)
(for/fold ([d (make-dict)])
([constr-decl (term decl)])
(dict-set d (first constr-decl) (third constr-decl)))
(term decl)
(term (c ...))))])
;; Merge two inductive declarations
(define-metafunction ttL
Δ-union : Δ Δ -> Δ
[(Δ-union Δ_1 Δ_2)
,(for/fold ([d (term Δ_1)])
([(k v) (in-dict (term Δ_2))])
(dict-set d k v))])
(define-metafunction ttL
Δ-set* : Δ (x t ((x : t) ...)) ... -> Δ
[(Δ-set* Δ) Δ]
[(Δ-set* Δ (D t_D ((c : t_c) ...)) any_r ...)
(Δ-set* (Δ-set Δ D t_D ((c : t_c) ...)) any_r ...)])
;; Returns the inductively defined type that x constructs
(define-metafunction ttL
Δ-key-by-constructor : Δ x -> x or #f
[(Δ-key-by-constructor Δ x_c)
,(for/or ([(k v) (in-dict (term Δ))])
(and (dict-has-key? (inductive-decl-constr-dict v) (term x_c)) k))])
;; Returns the constructor map for the inductively defined type x_D in the signature Δ
(define-metafunction ttL
Δ-ref-constructor-map : Δ x -> ((x : t) ...) or #f
[(Δ-ref-constructor-map Δ x_D)
,(cond
[(dict-ref (term Δ) (term x_D) (thunk #f)) =>
inductive-decl-constr-decl]
[else #f])])
;; Get the list of constructors for the inducitvely defined type x_D
(define-metafunction ttL
Δ-ref-constructors : Δ x -> (x ...) or #f
[(Δ-ref-constructors Δ x_D)
,(inductive-decl-constr-ls (dict-ref (term Δ) (term x_D)))])
;; NB: Depends on clause order
(define-metafunction ttL
sequence-index-of : any (any ...) -> natural
[(sequence-index-of any_0 (any_0 any ...))
0]
[(sequence-index-of any_0 (any_1 any ...))
,(add1 (term (sequence-index-of any_0 (any ...))))])
;; Get the index of the constructor x_ci in the list of constructors for x_D
(define-metafunction ttL
Δ-constructor-index : Δ x x -> natural
[(Δ-constructor-index Δ x_D x_ci)
(sequence-index-of x_ci (Δ-ref-constructors Δ x_D))])
;;; ------------------------------------------------------------------------
;;; Operations that involve contexts.
(define-extended-language tt-ctxtL ttL
;; Telescope.
;; NB: There is a bijection between this an a vector of maps from x to t
(Ξ Φ ::= hole (Π (x : t) Ξ))
;; Apply context
;; NB: There is a bijection between this an a vector expressions
(Θ ::= hole (Θ e)))
(define Ξ? (redex-match? tt-ctxtL Ξ))
(define Φ? (redex-match? tt-ctxtL Φ))
(define Θ? (redex-match? tt-ctxtL Θ))
;; TODO: Might be worth it to actually maintain the above bijections, for performance reasons.
;; Return the parameters of x_D as a telescope Ξ
(define-metafunction tt-ctxtL
Δ-ref-parameter-Ξ : Δ x -> Ξ
[(Δ-ref-parameter-Ξ Δ x_D)
Ξ
(where (in-hole Ξ U) (Δ-ref-type Δ x_D))])
;; Applies the term t to the telescope Ξ.
;; TODO: Test
#| TODO:
| This essentially eta-expands t at the type-level. Why is this necessary? Shouldn't it be true
| that (equivalent t (Ξ-apply Ξ t))?
| Maybe not. t is a lambda whose type is equivalent to (Ξ-apply Ξ t)? Yes.
|#
(define-metafunction tt-ctxtL
Ξ-apply : Ξ t -> t
[(Ξ-apply hole t) t]
[(Ξ-apply (Π (x : t) Ξ) t_0) (Ξ-apply Ξ (t_0 x))])
;; Compose multiple telescopes into a single telescope:
(define-metafunction tt-ctxtL
Ξ-compose : Ξ Ξ ... -> Ξ
[(Ξ-compose Ξ) Ξ]
[(Ξ-compose Ξ_0 Ξ_1 Ξ_rest ...)
(Ξ-compose (in-hole Ξ_0 Ξ_1) Ξ_rest ...)])
;; Compute the number of arguments in a Ξ
(define-metafunction tt-ctxtL
Ξ-length : Ξ -> natural
[(Ξ-length hole) 0]
[(Ξ-length (Π (x : t) Ξ)) ,(add1 (term (Ξ-length Ξ)))])
;; Compute the number of applications in a Θ
(define-metafunction tt-ctxtL
Θ-length : Θ -> natural
[(Θ-length hole) 0]
[(Θ-length (Θ e)) ,(add1 (term (Θ-length Θ)))])
;; Reference an expression in Θ by index; index 0 corresponds to the the expression applied to a hole.
(define-metafunction tt-ctxtL
Θ-ref : Θ natural -> e or #f
[(Θ-ref hole natural) #f]
[(Θ-ref (in-hole Θ (hole e)) 0) e]
[(Θ-ref (in-hole Θ (hole e)) natural) (Θ-ref Θ ,(sub1 (term natural)))])
;;; ------------------------------------------------------------------------
;;; Computing the types of eliminators
;; Returns the telescope of the arguments for the constructor x_ci of the inductively defined type x_D
(define-metafunction tt-ctxtL
Δ-constructor-telescope : Δ x x -> Ξ
[(Δ-constructor-telescope Δ x_D x_ci)
Ξ
(where (in-hole Ξ (in-hole Θ x_D))
(Δ-ref-constructor-type Δ x_D x_ci))])
;; Returns the parameter arguments as an apply context of the constructor x_ci of the inductively
;; defined type x_D
(define-metafunction tt-ctxtL
Δ-constructor-parameters : Δ x x -> Θ
[(Δ-constructor-parameters Δ x_D x_ci)
Θ
(where (in-hole Ξ (in-hole Θ x_D))
(Δ-ref-constructor-type Δ x_D x_ci))])
;; Inner loop for Δ-constructor-noninductive-telescope
(define-metafunction tt-ctxtL
noninductive-loop : x Φ -> Φ
[(noninductive-loop x_D hole) hole]
[(noninductive-loop x_D (Π (x : (in-hole Φ (in-hole Θ x_D))) Φ_1))
(noninductive-loop x_D Φ_1)]
[(noninductive-loop x_D (Π (x : t) Φ_1))
(Π (x : t) (noninductive-loop x_D Φ_1))])
;; Returns the noninductive arguments to the constructor x_ci of the inductively defined type x_D
(define-metafunction tt-ctxtL
Δ-constructor-noninductive-telescope : Δ x x -> Ξ
[(Δ-constructor-noninductive-telescope Δ x_D x_ci)
(noninductive-loop x_D (Δ-constructor-telescope Δ x_D x_ci))])
;; Inner loop for Δ-constructor-inductive-telescope
;; NB: Depends on clause order
(define-metafunction tt-ctxtL
inductive-loop : x Φ -> Φ
[(inductive-loop x_D hole) hole]
[(inductive-loop x_D (Π (x : (in-hole Φ (in-hole Θ x_D))) Φ_1))
(Π (x : (in-hole Φ (in-hole Θ x_D))) (inductive-loop x_D Φ_1))]
[(inductive-loop x_D (Π (x : t) Φ_1))
(inductive-loop x_D Φ_1)])
;; Returns the inductive arguments to the constructor x_ci of the inducitvely defined type x_D
(define-metafunction tt-ctxtL
Δ-constructor-inductive-telescope : Δ x x -> Ξ
[(Δ-constructor-inductive-telescope Δ x_D x_ci)
(inductive-loop x_D (Δ-constructor-telescope Δ x_D x_ci))])
;; Returns the inductive hypotheses required for eliminating the inductively defined type x_D with
;; motive t_P, where the telescope Φ are the inductive arguments to a constructor for x_D
(define-metafunction tt-ctxtL
hypotheses-loop : x t Φ -> Φ
[(hypotheses-loop x_D t_P hole) hole]
[(hypotheses-loop x_D t_P (name any_0 (Π (x : (in-hole Φ (in-hole Θ x_D))) Φ_1)))
;; TODO: Instead of this nonsense, it might be simpler to pass in the type of t_P and use that
;; as/to compute the type of the hypothesis.
(Π (x_h : (in-hole Φ ((in-hole Θ t_P) (Ξ-apply Φ x))))
(hypotheses-loop x_D t_P Φ_1))
(where x_h ,(variable-not-in (term (x_D t_P any_0)) 'x-ih))])
;; Returns the inductive hypotheses required for the elimination method of constructor x_ci for
;; inductive type x_D, when eliminating with motive t_P.
(define-metafunction tt-ctxtL
Δ-constructor-inductive-hypotheses : Δ x x t -> Ξ
[(Δ-constructor-inductive-hypotheses Δ x_D x_ci t_P)
(hypotheses-loop x_D t_P (Δ-constructor-inductive-telescope Δ x_D x_ci))])
(define-metafunction tt-ctxtL
Δ-constructor-method-telescope : Δ x x t -> Ξ
[(Δ-constructor-method-telescope Δ x_D x_ci t_P)
(Π (x_mi : (in-hole Ξ_a (in-hole Ξ_h ((in-hole Θ_p t_P) (Ξ-apply Ξ_a x_ci)))))
hole)
(where Θ_p (Δ-constructor-parameters Δ x_D x_ci))
(where Ξ_a (Δ-constructor-telescope Δ x_D x_ci))
(where Ξ_h (Δ-constructor-inductive-hypotheses Δ x_D x_ci t_P))
(where x_mi ,(variable-not-in (term (t_P Δ)) 'x-mi))])
;; fold Ξ-compose over map Δ-constructor-method-telescope over the list of constructors
(define-metafunction tt-ctxtL
method-loop : Δ x t (x ...) -> Ξ
[(method-loop Δ x_D t_P ()) hole]
[(method-loop Δ x_D t_P (x_0 x_rest ...))
(Ξ-compose (Δ-constructor-method-telescope Δ x_D x_0 t_P) (method-loop Δ x_D t_P (x_rest ...)))])
;; Returns the telescope of all methods required to eliminate the type x_D with motive t_P
(define-metafunction tt-ctxtL
Δ-methods-telescope : Δ x t -> Ξ
[(Δ-methods-telescope Δ x_D t_P)
(method-loop Δ x_D t_P (Δ-ref-constructors Δ x_D))])
;; Computes the type of the eliminator for the inductively defined type x_D with a motive whose result
;; is in universe U.
;;
;; The type of (elim x_D U) is something like:
;; (∀ (P : (∀ a -> ... -> (D a ...) -> U))
;; (method_ci ...) -> ... ->
;; (a -> ... -> (D a ...) ->
;; (P a ... (D a ...))))
;;
;; x_D is an inductively defined type
;; U is the sort the motive
;; x_P is the name of the motive
;; Ξ_P*D is the telescope of the parameters of x_D and
;; the witness of type x_D (applied to the parameters)
;; Ξ_m is the telescope of the methods for x_D
(define-metafunction tt-ctxtL
Δ-elim-type : Δ x U -> t
[(Δ-elim-type Δ x_D U)
(Π (x_P : (in-hole Ξ_P*D U))
;; The methods Ξ_m for each constructor of type x_D
(in-hole Ξ_m
;; And finally, the parameters and discriminant
(in-hole Ξ_P*D
;; The result is (P a ... (x_D a ...)), i.e., the motive
;; applied to the paramters and discriminant
(Ξ-apply Ξ_P*D x_P))))
;; Get the parameters of x_D
(where Ξ (Δ-ref-parameter-Ξ Δ x_D))
;; A fresh name to bind the discriminant
(where x ,(variable-not-in (term (Δ Γ x_D Ξ)) 'x-D))
;; The telescope (∀ a -> ... -> (D a ...) hole), i.e.,
;; of the parameters and the inductive type applied to the
;; parameters
(where Ξ_P*D (in-hole Ξ (Π (x : (Ξ-apply Ξ x_D)) hole)))
;; A fresh name for the motive
(where x_P ,(variable-not-in (term (Δ Γ x_D Ξ Ξ_P*D x)) 'x-P))
;; The types of the methods for this inductive.
(where Ξ_m (Δ-methods-telescope Δ x_D x_P))])
;; TODO: This might belong in the next section, since it's related to evaluation
;; Generate recursive applications of the eliminator for each inductive argument of type x_D.
;; In more detaill, given motive t_P, parameters Θ_p, methods Θ_m, and arguments Θ_i to constructor
;; x_ci for x_D, for each inductively smaller term t_i of type (in-hole Θ_p x_D) inside Θ_i,
;; generate: (elim x_D U t_P Θ_m ... Θ_p ... t_i)
(define-metafunction tt-ctxtL
Δ-inductive-elim : Δ x U t Θ Θ Θ -> Θ
[(Δ-inductive-elim Δ x_D U t_P Θ_p Θ_m (in-hole Θ_i (hole (name t_i (in-hole Θ_r x_ci)))))
((Δ-inductive-elim Δ x_D U t_P Θ_p Θ_m Θ_i)
(in-hole ((in-hole Θ_p Θ_m) t_i) ((elim x_D U) t_P)))
(side-condition (memq (term x_ci) (term (Δ-ref-constructors Δ x_D))))]
[(Δ-inductive-elim Δ x_D U t_P Θ_p Θ_m Θ_nr) hole])
;;; ------------------------------------------------------------------------
;;; Dynamic semantics
;;; The majority of this section is dedicated to evaluation of (elim x U), the eliminator for the
;;; inductively defined type x with a motive whose result is in universe U
(define-extended-language tt-redL tt-ctxtL
;; NB: (in-hole Θv (elim x U)) is only a value when it's a partially applied elim. However,
;; determining whether or not it is partially applied cannot be done with the grammar alone.
(v ::= x U (Π (x : t) t) (λ (x : t) t) (elim x U) (in-hole Θv x) (in-hole Θv (elim x U)))
(Θv ::= hole (Θv v))
;; call-by-value, plus reduce under Π (helps with typing checking)
(E ::= hole (E e) (v E) (Π (x : v) E) (Π (x : E) e)))
(define Θv? (redex-match? tt-redL Θv))
(define E? (redex-match? tt-redL E))
(define v? (redex-match? tt-redL v))
(define current-Δ (make-parameter (make-empty-Δ)))
(define tt-->
(reduction-relation tt-redL
(--> (in-hole E ((λ (x : t_0) t_1) t_2))
(in-hole E (subst t_1 x t_2))
-->β)
(--> (in-hole E (in-hole Θv ((elim x_D U) v_P)))
(in-hole E (in-hole Θ_r (in-hole Θv_i v_mi)))
#|
| The elim form must appear applied like so:
| (elim x_D U v_P m_0 ... m_i m_j ... m_n p ... (c_i a ...))
|
| Where:
| x_D is the inductive being eliminated
| U is the universe of the result of the motive
| v_P is the motive
| m_{0..n} are the methods
| p ... are the parameters of x_D
| c_i is a constructor of x_d
| a ... are the arguments to c_i
| Unfortunately, Θ contexts turn all this inside out:
| TODO: Write better abstractions for this notation
|#
(where Δ ,(current-Δ))
;; Split Θv into its components: the paramters Θv_P for x_D, the methods Θv_m for x_D, and
;; the discriminant: the constructor x_ci applied to its argument Θv_i
(where (in-hole (Θv_p (in-hole Θv_i x_ci)) Θv_m) Θv)
;; Check that Θ_p actually matches the parameters of x_D, to ensure it doesn't capture other
;; arguments.
(side-condition (equal? (term (Θ-length Θv_p)) (term (Ξ-length (Δ-ref-parameter-Ξ Δ x_D)))))
;; Ensure x_ci is actually a constructor for x_D
(where (x_c* ...) (Δ-ref-constructors Δ x_D))
(side-condition (memq (term x_ci) (term (x_c* ...))))
;; There should be a number of methods equal to the number of constructors; to ensure E
;; doesn't capture methods and Θv_m doesn't capture other arguments
(side-condition (equal? (length (term (x_c* ...))) (term (Θ-length Θv_m))))
;; Find the method for constructor x_ci, relying on the order of the arguments.
(where v_mi (Θ-ref Θv_m (Δ-constructor-index Δ x_D x_ci)))
;; Generate the inductive recursion
(where Θ_r (Δ-inductive-elim Δ x_D U v_P Θv_p Θv_m Θv_i))
-->elim)))
(define reduce-memoize (make-hash))
(define-metafunction tt-redL
step : Δ e -> e
[(step Δ e)
e_r
(where e_r ,(dict-ref reduce-memoize (term e)
(thunk
(parameterize ([current-Δ (term Δ)])
(let ([x (car (apply-reduction-relation tt--> (term e)))])
(dict-set! reduce-memoize (term e_r) x)
x)))))])
(define-metafunction tt-redL
reduce : Δ e -> e
[(reduce Δ e)
e_r
(where e_r ,(dict-ref reduce-memoize (term e)
(thunk
(parameterize ([current-Δ (term Δ)])
(let ([x (car (apply-reduction-relation* tt--> (term e) #:cache-all? #t))])
(dict-set! reduce-memoize (term e_r) x)
x)))))])
(define-judgment-form tt-redL
#:mode (equivalent I I I)
#:contract (equivalent Δ t t)
[(where t_2 (reduce Δ t_0))
(where t_3 (reduce Δ t_1))
(side-condition (α-equivalent? t_2 t_3))
----------------- "≡-αβ"
(equivalent Δ t_0 t_1)])
;;; ------------------------------------------------------------------------
;;; Type checking and synthesis
(define-extended-language tt-typingL tt-redL
;; NB: There may be a bijection between Γ and Ξ. That's interesting.
;; NB: Also a bijection between Γ and a list of maps from x to t.
(Γ ::= (Γ x : t)))
(define Γ? (redex-match? tt-typingL Γ))
(define-metafunction tt-typingL
Γ-union : Γ Γ -> Γ
[(Γ-union Γ ) Γ]
[(Γ-union Γ_2 (Γ_1 x : t))
((Γ-union Γ_2 Γ_1) x : t)])
(define-metafunction tt-typingL
Γ-set : Γ x t -> Γ
[(Γ-set Γ x t) (Γ x : t)])
;; NB: Depends on clause order
(define-metafunction tt-typingL
Γ-ref : Γ x -> t or #f
[(Γ-ref x) #f]
[(Γ-ref (Γ x : t) x) t]
[(Γ-ref (Γ x_0 : t_0) x_1) (Γ-ref Γ x_1)])
;; NB: Depends on clause order
(define-metafunction tt-typingL
Γ-remove : Γ x -> Γ
[(Γ-remove x) ]
[(Γ-remove (Γ x : t) x) Γ]
[(Γ-remove (Γ x_0 : t_0) x_1) (Γ-remove Γ x_1)])
(define-metafunction tt-typingL
nonpositive : x t -> #t or #f
[(nonpositive x (in-hole Θ x))
#t]
[(nonpositive x (Π (x_0 : (in-hole Θ x)) t))
#f]
[(nonpositive x (Π (x_0 : t_0) t))
,(and (term (positive x t_0)) (term (nonpositive x t)))]
[(nonpositive x t) #t])
(define-metafunction tt-typingL
positive : x t -> #t or #f
[(positive x (in-hole Θ x))
#f]
[(positive x (Π (x_0 : (in-hole Θ x)) t))
(positive x t)]
[(positive x (Π (x_0 : t_0) t))
,(and (term (nonpositive x t_0)) (term (positive x t)))]
[(positive x t) #t])
(define-metafunction tt-typingL
positive* : x (t ...) -> #t or #f
[(positive* x_D ()) #t]
[(positive* x_D (t_c t_rest ...))
;; Replace the result of the constructor with (Unv 0), to avoid the result being considered a
;; nonpositive position.
,(and (term (positive x_D (in-hole Ξ (Unv 0)))) (term (positive* x_D (t_rest ...))))
(where (in-hole Ξ (in-hole Θ x_D)) t_c)])
;; Holds when the signature Δ and typing context Γ are well-formed.
(define-judgment-form tt-typingL
#:mode (wf I I)
#:contract (wf Δ Γ)
[(side-condition ,(empty-Δ? (term Δ)))
----------------- "WF-Empty"
(wf Δ )]
[(type-infer Δ Γ t t_0)
(wf Δ Γ)
----------------- "WF-Var"
(wf Δ (Γ x : t))]
[(side-condition ,(not (empty-Δ? (term Δ_1))))
;; TODO: Depends on order, but "first" here is nondeterministic/unspecified
(where x_D ,(dict-iterate-key (term Δ_1) (dict-iterate-first (term Δ_1))))
(where t_D (Δ-ref-type Δ_1 x_D))
(where (x_c ...) (Δ-ref-constructors Δ_1 x_D))
(where ((name t_c (in-hole Ξ (in-hole Θ x_D*))) ...)
((Δ-ref-type Δ_1 x_c) ...))
(where Δ ,(dict-remove (term Δ_1) (term x_D)))
(wf Δ )
(type-infer Δ t_D U_D)
(type-infer Δ ( x_D : t_D) t_c U_c) ...
;; NB: Ugh this should be possible with pattern matching alone ....
(side-condition ,(map (curry equal? (term x_D)) (term (x_D* ...))))
(side-condition (positive* x_D (t_c ...)))
----------------- "WF-Inductive"
(wf Δ_1 )])
;; TODO: Bi-directional and inference?
;; TODO: http://www.cs.ox.ac.uk/ralf.hinze/WG2.8/31/slides/stephanie.pdf
;; Holds when e has type t under signature Δ and typing context Γ
(define-judgment-form tt-typingL
#:mode (type-infer I I I O)
#:contract (type-infer Δ Γ e t)
[(wf Δ Γ)
(unv-type U_0 U_1)
----------------- "DTR-Unv"
(type-infer Δ Γ U_0 U_1)]
[(wf Δ Γ)
(where t (Δ-ref-type Δ x))
----------------- "DTR-Inductive"
(type-infer Δ Γ x t)]
[(wf Δ Γ)
(where t (Γ-ref Γ x))
----------------- "DTR-Start"
(type-infer Δ Γ x t)]
[(type-infer Δ (Γ x : t_0) e t_1)
(type-infer Δ Γ (Π (x : t_0) t_1) U)
----------------- "DTR-Abstraction"
(type-infer Δ Γ (λ (x : t_0) e) (Π (x : t_0) t_1))]
[(type-infer Δ Γ t_0 U_1)
(type-infer Δ (Γ x : t_0) t U_2)
(unv-pred U_1 U_2 U)
----------------- "DTR-Product"
(type-infer Δ Γ (Π (x : t_0) t) U)]
[(type-infer Δ Γ e_0 t)
;; Cannot rely on type-infer producing normal forms.
(where (Π (x_0 : t_0) t_1) (reduce Δ t))
(type-check Δ Γ e_1 t_0)
(where t_3 (subst t_1 x_0 e_1))
----------------- "DTR-Application"
(type-infer Δ Γ (e_0 e_1) t_3)]
[(where t (Δ-elim-type Δ D U))
(type-infer Δ Γ t U_e)
----------------- "DTR-Elim_D"
(type-infer Δ Γ (elim D U) t)])
(define-judgment-form tt-typingL
#:mode (type-check I I I I)
#:contract (type-check Δ Γ e t)
[(type-infer Δ Γ e t_0)
(equivalent Δ t t_0)
----------------- "DTR-Check"
(type-check Δ Γ e t)])
(module+ test
(require rackunit)
(define-term Δt (Δ-set ,(make-empty-Δ) True (Unv 0) ((T : True))))
(check-false
(judgment-holds (type-check ,(make-empty-Δ) T True)))
(check-true
(judgment-holds (type-check Δt T True)))
(check-true
(judgment-holds (type-check Δt True (Unv 0))))
(check-true
(judgment-holds (wf Δt )))
(check-true
(judgment-holds (wf Δt ( P : (Unv 0)))))
(check-true
(judgment-holds (type-infer Δt (Π (P : (Unv 0)) True) t))))

View File

@ -0,0 +1,493 @@
#lang racket/base
;; This module just provide module language sugar over the redex model.
(require
"hybrid-core.rkt"
redex/reduction-semantics
racket/provide-syntax
(for-syntax
(except-in racket import)
syntax/parse
racket/syntax
(except-in racket/provide-transform export)
racket/require-transform
"hybrid-core.rkt"
redex/reduction-semantics))
(provide
;; Basic syntax
for-syntax
only-in
except-in
all-defined-out
rename-in
rename-out
prefix-out
prefix-in
submod
#%module-begin
(rename-out
[dep-module+ module+]
[dep-provide provide]
[dep-require require]
[dep-lambda lambda]
[dep-lambda λ]
[dep-app #%app]
[dep-forall forall]
[dep-forall ]
[dep-inductive data]
[dep-elim elim]
[dep-top #%top]
[dep-top-interaction #%top-interaction]
; [dep-datum #%datum]
[dep-define define]
[dep-void void])
begin
Type
;; DYI syntax extension
define-syntax
begin-for-syntax
define-for-syntax
syntax-case
syntax-rules
define-syntax-rule
(for-syntax
(all-from-out syntax/parse)
(all-from-out racket)
(all-from-out racket/syntax)
cur->datum
cur-expand
type-infer/syn
type-check/syn?
normalize/syn
step/syn
cur-equal?))
(begin-for-syntax
;; TODO: Gamma and Delta seem to get reset inside a module+
(define gamma
(make-parameter
(term )
(lambda (x)
(unless (Γ? x)
(error 'core-error "We built a bad term environment ~s" x))
x)))
(define delta
(make-parameter
(make-empty-Δ)
(lambda (x)
(unless (Δ? x)
(error 'core-error "We built a bad inductive declaration ~s" x))
x)))
;; These should be provided by core, so details of envs can be hidden.
(define (extend-Γ/term env x t)
(term (Γ-set ,(env) ,x ,t)))
(define (extend-Γ/term! env x t) (env (extend-Γ/term env x t)))
(define (extend-Γ/syn env x t)
(extend-Γ/term env (syntax->datum x) (cur->datum t)))
(define (extend-Γ/syn! env x t) (env (extend-Γ/syn env x t)))
(define (extend-Δ/term env x t c*)
(term (Δ-set ,(env) ,x ,t (,@c*))))
(define (extend-Δ/term! env x t c*)
(env (extend-Δ/term env x t c*)))
(define (extend-Δ/syn env x t c*)
(extend-Δ/term env (syntax->datum x) (cur->datum t)
(for/list ([c (syntax->list c*)])
(syntax-case c ()
[(c : ct)
(parameterize ([gamma (extend-Γ/syn gamma x t)])
(term (,(syntax->datum #'c) : ,(cur->datum #'ct))))]))))
(define (extend-Δ/syn! env x t c*)
(env (extend-Δ/syn env x t c*)))
(define subst? (list/c (listof x?) (listof e?)))
(define bind-subst
(make-parameter
(list null null)
(lambda (x)
(unless (subst? x)
(error 'core-error "We build a bad subst ~s" x))
x)))
(define (add-binding/term! x t)
(let ([vars (first (bind-subst))]
[exprs (second (bind-subst))])
(bind-subst (list (cons x vars) (cons t exprs)))))
(define (subst-bindings t)
(term (subst-all ,t ,(first (bind-subst)) ,(second (bind-subst)))))
(define (type-infer/term t)
(let ([t (judgment-holds (type-infer ,(delta) ,(gamma) ,(subst-bindings t) t_0) t_0)])
(and (pair? t) (car t))))
(define (type-check/term? e t)
(and (judgment-holds (type-check ,(delta) ,(gamma) ,(subst-bindings e) ,(subst-bindings t))) #t))
;; TODO: Blanket disarming is probably a bad idea.
(define orig-insp (variable-reference->module-declaration-inspector (#%variable-reference)))
(define (disarm syn) (syntax-disarm syn orig-insp))
;; Locally expand everything down to core forms.
(define (core-expand syn)
(disarm
(local-expand
syn
'expression
(append (syntax-e #'(term reduce subst-all dep-top #%app λ Π elim Unv #%datum void))))))
;; Only type-check at the top-level, to prevent exponential
;; type-checking. Redex is expensive enough.
(define inner-expand? (make-parameter #f))
;; Reifiy cur syntax into curnel datum
(define (cur->datum syn)
;; Main loop; avoid type
(define reified-term
;; TODO: This results in less good error messages. Add an
;; algorithm to find the smallest ill-typed term.
(parameterize ([inner-expand? #t])
(let cur->datum ([syn syn])
(syntax-parse (core-expand syn)
#:literals (term reduce #%app subst-all)
#:datum-literals (elim Π λ : Unv)
[x:id (syntax->datum #'x)]
[(subst-all e _ _) (syntax->datum #'e)]
[(reduce Δ e) (cur->datum #'e)]
[(term e) (cur->datum #'e)]
[(Unv i) (term (Unv ,(syntax->datum #'i)))]
;; TODO: should really check that b is one of the binders
;; Maybe make a syntax class for the binders, core forms,
;; etc.
[(b:id (x:id : t) e)
(let* ([x (syntax->datum #'x)]
[t (cur->datum #'t)]
[e (parameterize ([gamma (extend-Γ/term gamma x t)])
(cur->datum #'e))])
(term (,(syntax->datum #'b) (,x : ,t) ,e)))]
[(elim t1 t2)
(let* ([t1 (cur->datum #'t1)]
[t2 (cur->datum #'t2)])
(term (elim ,t1 ,t2)))]
[(#%app e1 e2)
(term (,(cur->datum #'e1) ,(cur->datum #'e2)))]))))
(unless (or (inner-expand?) (type-infer/term reified-term))
(printf "Delta: ~s~nGamma: ~s~n" (delta) (gamma))
(raise-syntax-error 'cur "term is ill-typed:" reified-term syn))
reified-term)
(define (datum->cur syn t)
(match t
[(list (quote term) e)
(quasisyntax/loc
syn
(datum->cur syn e))]
[(list (quote Unv) i)
(quasisyntax/loc
syn
(Type #,i))]
[(list (quote Π) (list x (quote :) t) body)
(quasisyntax/loc
syn
(dep-forall (#,(datum->syntax syn x) : #,(datum->cur syn t)) #,(datum->cur syn body)))]
[(list (quote λ) (list x (quote :) t) body)
(quasisyntax/loc
syn
(dep-lambda (#,(datum->syntax syn x) : #,(datum->cur syn t)) #,(datum->cur syn body)))]
[(list (list (quote elim) t1) t2)
(quasisyntax/loc
syn
(dep-elim #,(datum->cur syn t1) #,(datum->cur syn t2)))]
[(list e1 e2)
(quasisyntax/loc
syn
(dep-app #,(datum->cur syn e1) #,(datum->cur syn e2)))]
[_
(quasisyntax/loc
syn
#,(datum->syntax syn t))]))
(define (eval-cur syn)
(term (reduce ,(delta) ,(subst-bindings (cur->datum syn)))))
(define (syntax->curnel-syntax syn)
(quasisyntax/loc
syn
;; TODO: this subst-all should be #,(subst-bindings (cur->datum syn)), but doesn't work
(term (reduce #,(delta) (subst-all #,(cur->datum syn) #,(first (bind-subst)) #,(second (bind-subst)))))))
;; Reflection tools
(define (normalize/syn syn)
(datum->cur
syn
(eval-cur syn)))
(define (step/syn syn)
(datum->cur
syn
(term (step ,(delta) ,(subst-bindings (cur->datum syn))))))
;; Are these two terms equivalent in type-systems internal equational reasoning?
(define (cur-equal? e1 e2)
(and (judgment-holds (equivalent ,(delta) ,(eval-cur e1) ,(eval-cur e2))) #t))
;; TODO: Document local-env
(define (type-infer/syn syn #:local-env [env '()])
(parameterize ([gamma (for/fold ([gamma (gamma)])
([(x t) (in-dict env)])
(extend-Γ/syn (thunk gamma) x t))])
(let ([t (type-infer/term (eval-cur syn))])
(and t (datum->cur syn t)))))
(define (type-check/syn? syn type)
(type-check/term? (eval-cur syn) (eval-cur type)))
;; Takes a Cur term syn and an arbitrary number of identifiers ls. The cur term is
;; expanded until expansion reaches a Curnel form, or one of the
;; identifiers in ls.
(define (cur-expand syn . ls)
(disarm
(local-expand
syn
'expression
(append (syntax-e #'(Type dep-inductive dep-lambda dep-app dep-elim dep-forall dep-top))
ls)))))
;; -----------------------------------------------------------------
;; Require/provide macros
#| TODO NB XXX
| This is code some of the most hacky awful code I've ever written. But it works. ish
|#
(begin-for-syntax
(define envs (list #'(void)))
(define (cur-identifier-bound? id)
(let ([x (syntax->datum id)])
(and (x? x)
(or (term (Γ-ref ,(gamma) ,x))
(term (Δ-ref-type ,(delta) ,x))))))
(define (filter-cur-exports syn modes)
(partition (compose cur-identifier-bound? export-local-id)
(apply append (map (lambda (e) (expand-export e modes))
(syntax->list syn))))))
(define-syntax extend-env-and-provide
(make-provide-transformer
(lambda (syn modes)
(syntax-case syn ()
[(_ e ...)
(let-values ([(cur ~cur) (filter-cur-exports #'(e ...) modes)])
#| TODO: Ignoring the built envs for now
(set! envs (for/list ([e cur])
(let* ([x (syntax->datum (export-local-id e))]
[t (type-infer/term x)]
[env (if (term (lookup ,(gamma) ,x)) #'gamma #'delta)])
#`(extend-env/term! #,env #,(export-out-sym e) #,t))))
|#
~cur)]))))
(define-syntax (export-envs syn)
(syntax-case syn ()
[(_ gamma-out delta-out bind-out)
(begin
#`(begin-for-syntax
(define gamma-out (term #,(gamma)))
(define delta-out (term #,(delta)))
(define bind-out '#,(bind-subst))))]))
;; TODO: This can only handle a single provide form, otherwise generates multiple *-out
(define-syntax (dep-provide syn)
(syntax-case syn ()
[(_ e ...)
(begin
#| TODO NB:
| Ignoring the built envs above, for now. The local-lift export seems to get executed before
| the filtered environment is built.
|#
;; TODO: rename out will need to rename variables in gamma and ; delta.
(syntax-local-lift-module-end-declaration
#`(export-envs gamma-out delta-out bind-out))
#`(provide (extend-env-and-provide e ...)
(for-syntax gamma-out delta-out bind-out)))]))
(begin-for-syntax
(define out-gammas #`())
(define out-deltas #`())
(define out-binds #`())
(define gn 0)
(define sn 0)
(define bn 0)
(define (filter-cur-imports syn)
(for/fold ([imports '()]
[sources '()])
([req-spec (syntax->list syn)])
(let-values ([(more-imports more-sources) (expand-import req-spec)])
(values (for/fold ([imports imports])
([imp more-imports])
(cond
[(equal? (import-src-sym imp) 'gamma-out)
(let ([new-id (format-id (import-orig-stx imp)
"gamma-out~a" gn)])
;; TODO: Fewer set!s
;; TODO: Do not DIY gensym
(set! gn (add1 gn))
(set! out-gammas
#`(#,@out-gammas (gamma (term (Γ-union
,(gamma)
,#,new-id)))))
(cons (struct-copy import imp [local-id new-id])
imports))]
;; TODO: Many shared code between these two clauses
[(equal? (import-src-sym imp) 'delta-out)
(let ([new-id (format-id (import-orig-stx imp)
"delta-out~a" sn)])
;; TODO: Fewer set!s
;; TODO: Do not DIY gensym
(set! sn (add1 sn))
(set! out-deltas
#`(#,@out-deltas (delta (term (Δ-union
,(delta)
,#,new-id)))))
(cons (struct-copy import imp [local-id new-id])
imports))]
;; TODO: Many shared code between these two clauses
[(equal? (import-src-sym imp) 'bind-out)
(let ([new-id (format-id (import-orig-stx imp)
"bind-out~a" bn)])
;; TODO: Fewer set!s
;; TODO: Do not DIY gensym
(set! bn (add1 bn))
(set! out-binds
#`(#,@out-binds (bind-subst (list (append
(first #,new-id)
(first (bind-subst)))
(append
(second #,new-id)
(second (bind-subst)))))))
(cons (struct-copy import imp [local-id new-id])
imports))]
[else (cons imp imports)]))
(append sources more-sources))))))
(define-syntax extend-env-and-require
(make-require-transformer
(lambda (syn)
(syntax-case syn ()
[(_ e ...) (filter-cur-imports #'(e ...))]))))
;; TODO: rename in will need to rename variables in gamma and delta.
(define-syntax (import-envs syn)
(syntax-case syn ()
[(_) #`(begin-for-syntax #,@out-gammas #,@out-deltas
#,@out-binds)]))
(define-syntax (dep-require syn)
(syntax-case syn ()
[(_ e ...)
#`(begin
(require (extend-env-and-require e ...))
(import-envs))]))
(define-syntax (dep-module+ syn)
(syntax-case syn ()
[(_ name body ...)
#`(module+ name
(begin-for-syntax
(gamma (term #,(gamma)))
(delta (term #,(delta)))
(bind-subst '#,(bind-subst)))
body ...)]))
;; -----------------------------------------------------------------
;; Core wrapper macros
;;
;; TODO: Can these be simplified further?
(define-syntax (dep-lambda syn)
(syntax-case syn (:)
[(_ (x : t) e)
(syntax->curnel-syntax
(quasisyntax/loc syn (λ (x : t) e)))]))
(define-syntax (dep-app syn)
(syntax-case syn ()
[(_ e1 e2)
(syntax->curnel-syntax
(quasisyntax/loc syn (#%app e1 e2)))]))
(define-syntax (dep-forall syn)
(syntax-case syn (:)
[(_ (x : t) e)
(syntax->curnel-syntax
(quasisyntax/loc syn (Π (x : t) e)))]))
(define-syntax (Type syn)
(syntax-case syn ()
[(_ i)
(syntax->curnel-syntax
(quasisyntax/loc syn (Unv i)))]
[_ (quasisyntax/loc syn (Type 0))]))
(define-syntax (dep-inductive syn)
(syntax-case syn (:)
[(_ i : ti (x1 : t1) ...)
(begin
(extend-Δ/syn! delta #'i #'ti #'((x1 : t1) ...))
#'(void))]))
(define-syntax (dep-elim syn)
(syntax-case syn ()
[(_ D T)
(syntax->curnel-syntax
(quasisyntax/loc syn (elim D T)))]))
(define-syntax-rule (dep-void) (void))
;; TODO: Not sure if this is the correct behavior for #%top
(define-syntax (dep-top syn)
(syntax-case syn ()
[(_ . id)
;; TODO NB FIXME: HACKS HACKS HACKS
(let ([t (core-expand #'id)])
(if (equal? (syntax->datum t) '(void))
#'(void)
(syntax->curnel-syntax t)))]))
(define-syntax (dep-top-interaction syn)
(syntax-case syn ()
[(_ . form)
(begin
;; TODO NB FIXME: HACKS
#`(begin
(export-envs gamma-out delta-out bind-out)
(begin-for-syntax
(define nm (map (lambda (x) (namespace-variable-value x #f (lambda x #t))) (namespace-mapped-symbols)))
(bind-subst (first (memf subst? nm)))
(gamma (first (memf Γ? nm)))
(delta (first (memf Δ? nm))))
form))]))
(define-syntax (dep-define syn)
(syntax-parse syn
[(_ id:id e)
(let ([e (cur->datum #'e)]
[id (syntax->datum #'id)])
;; NB: Have to roll our own namespace rather than use built-in define so id is resolved at
;; compile time in redex, and at runtime in racket.
(extend-Γ/term! gamma id (type-infer/term e))
(add-binding/term! id e)
#'(void))]))

View File

@ -2,7 +2,6 @@
(require
racket/function
racket/list
redex/reduction-semantics)
(provide
@ -26,11 +25,11 @@
(define-language ttL
(i j k ::= natural)
(U ::= (Unv i))
(D x c ::= variable-not-otherwise-mentioned)
(t e ::= U (λ (x : t) e) x (Π (x : t) t) (e e) (elim D U))
;; Δ (signature). (inductive-name : type ((constructor : type) ...))
;; NB: Δ is a map from a name x to a pair of it's type and a map of constructor names to their types
(Δ ::= (Δ (D : t ((c : t) ...))))
(t e ::= U (λ (x : t) e) x (Π (x : t) t) (e e)
;; (elim inductive-type motive (indices ...) (methods ...) discriminant)
(elim D e (e ...) (e ...) e))
(D x c ::= variable-not-otherwise-mentioned)
#:binding-forms
(λ (x : t) e #:refers-to x)
(Π (x : t_0) t_1 #:refers-to x))
@ -44,8 +43,6 @@
;;; ------------------------------------------------------------------------
;;; Universe typing
;; Universe types
;; aka Axioms A of a PTS
(define-judgment-form ttL
#:mode (unv-type I O)
#:contract (unv-type U U)
@ -55,7 +52,6 @@
(unv-type (Unv i_0) (Unv i_1))])
;; Universe predicativity rules. Impredicative in (Unv 0)
;; aka Rules R of a PTS
(define-judgment-form ttL
#:mode (unv-pred I I O)
#:contract (unv-pred U U U)
@ -108,6 +104,25 @@
[(Δ-union Δ_2 (Δ_1 (x : t any)))
((Δ-union Δ_2 Δ_1) (x : t any))])
;; Returns the inductively defined type that x constructs
;; NB: Depends on clause order
(define-metafunction ttL
Δ-key-by-constructor : Δ x -> x
[(Δ-key-by-constructor (Δ (x : t ((x_0 : t_0) ... (x_c : t_c) (x_1 : t_1) ...))) x_c)
x]
[(Δ-key-by-constructor (Δ (x_1 : t_1 any)) x)
(Δ-key-by-constructor Δ x)])
;; Returns the constructor map for the inductively defined type x_D in the signature Δ
(define-metafunction ttL
Δ-ref-constructor-map : Δ x -> ((x : t) ...) or #f
;; NB: Depends on clause order
[(Δ-ref-constructor-map x_D) #f]
[(Δ-ref-constructor-map (Δ (x_D : t_D any)) x_D)
any]
[(Δ-ref-constructor-map (Δ (x_1 : t_1 any)) x_D)
(Δ-ref-constructor-map Δ x_D)])
;; TODO: Should not use Δ-ref-type
(define-metafunction ttL
Δ-ref-constructor-type : Δ x x -> t
@ -124,9 +139,6 @@
[(Δ-ref-constructors (Δ (x_1 : t_1 any)) x_D)
(Δ-ref-constructors Δ x_D)])
;; TODO: Mix of pure Redex/escaping to Racket sometimes is getting confusing.
;; TODO: Justify, or stop.
;; NB: Depends on clause order
(define-metafunction ttL
sequence-index-of : any (any ...) -> natural
@ -158,35 +170,45 @@
;; TODO: Might be worth it to actually maintain the above bijections, for performance reasons.
;; Return the parameters of x_D as a telescope Ξ
;; TODO: Define generic traversals of Δ and Γ ?
(define-metafunction tt-ctxtL
Δ-ref-parameter-Ξ : Δ x -> Ξ
[(Δ-ref-parameter-Ξ (Δ (x_D : (in-hole Ξ U) any)) x_D)
Ξ]
[(Δ-ref-parameter-Ξ (Δ (x_1 : t_1 any)) x_D)
(Δ-ref-parameter-Ξ Δ x_D)])
;; Applies the term t to the telescope Ξ.
;; TODO: Test
#| TODO:
| This essentially eta-expands t at the type-level. Why is this necessary? Shouldn't it be true
| that (convert t (Ξ-apply Ξ t))?
| Maybe not. t is a lambda whose type is convert to (Ξ-apply Ξ t)? Yes.
| that (equivalent t (Ξ-apply Ξ t))?
| Maybe not. t is a lambda whose type is equivalent to (Ξ-apply Ξ t)? Yes.
|#
(define-metafunction tt-ctxtL
Ξ-apply : Ξ t -> t
[(Ξ-apply hole t) t]
[(Ξ-apply (Π (x : t) Ξ) t_0) (Ξ-apply Ξ (t_0 x))])
;; Compose multiple telescopes into a single telescope:
(define-metafunction tt-ctxtL
Ξ-compose : Ξ Ξ ... -> Ξ
[(Ξ-compose Ξ) Ξ]
[(Ξ-compose Ξ_0 Ξ_1 Ξ_rest ...)
(Ξ-compose (in-hole Ξ_0 Ξ_1) Ξ_rest ...)])
;; Compute the number of arguments in a Ξ
(define-metafunction tt-ctxtL
Ξ-length : Ξ -> natural
[(Ξ-length hole) 0]
[(Ξ-length (Π (x : t) Ξ)) ,(add1 (term (Ξ-length Ξ)))])
;; Compute the number of applications in a Θ
(define-metafunction tt-ctxtL
list->Θ : (e ...) -> Θ
[(list->Θ ()) hole]
[(list->Θ (e e_r ...))
(in-hole (list->Θ (e_r ...)) (hole e))])
(define-metafunction tt-ctxtL
apply : e e ... -> e
[(apply e_f e ...)
(in-hole (list->Θ (e ...)) e_f)])
Θ-length : Θ -> natural
[(Θ-length hole) 0]
[(Θ-length (Θ e)) ,(add1 (term (Θ-length Θ)))])
;; Reference an expression in Θ by index; index 0 corresponds to the the expression applied to a hole.
(define-metafunction tt-ctxtL
@ -198,17 +220,6 @@
;;; ------------------------------------------------------------------------
;;; Computing the types of eliminators
;; Return the parameters of x_D as a telescope Ξ
;; TODO: Define generic traversals of Δ and Γ ?
(define-metafunction tt-ctxtL
Δ-ref-parameter-Ξ : Δ x -> Ξ or #f
[(Δ-ref-parameter-Ξ (Δ (x_D : (in-hole Ξ U) any)) x_D)
Ξ]
[(Δ-ref-parameter-Ξ (Δ (x_1 : t_1 any)) x_D)
(Δ-ref-parameter-Ξ Δ x_D)]
[(Δ-ref-parameter-Ξ Δ x)
#f])
;; Returns the telescope of the arguments for the constructor x_ci of the inductively defined type x_D
(define-metafunction tt-ctxtL
Δ-constructor-telescope : Δ x x -> Ξ
@ -226,6 +237,21 @@
(where (in-hole Ξ (in-hole Θ x_D))
(Δ-ref-constructor-type Δ x_D x_ci))])
;; Inner loop for Δ-constructor-noninductive-telescope
(define-metafunction tt-ctxtL
noninductive-loop : x Φ -> Φ
[(noninductive-loop x_D hole) hole]
[(noninductive-loop x_D (Π (x : (in-hole Φ (in-hole Θ x_D))) Φ_1))
(noninductive-loop x_D Φ_1)]
[(noninductive-loop x_D (Π (x : t) Φ_1))
(Π (x : t) (noninductive-loop x_D Φ_1))])
;; Returns the noninductive arguments to the constructor x_ci of the inductively defined type x_D
(define-metafunction tt-ctxtL
Δ-constructor-noninductive-telescope : Δ x x -> Ξ
[(Δ-constructor-noninductive-telescope Δ x_D x_ci)
(noninductive-loop x_D (Δ-constructor-telescope Δ x_D x_ci))])
;; Inner loop for Δ-constructor-inductive-telescope
;; NB: Depends on clause order
(define-metafunction tt-ctxtL
@ -254,6 +280,36 @@
(hypotheses-loop x_D t_P Φ_1))
(where x_h ,(variable-not-in (term (x_D t_P any_0)) 'x-ih))])
;; Returns the inductive hypotheses required for the elimination method of constructor x_ci for
;; inductive type x_D, when eliminating with motive t_P.
(define-metafunction tt-ctxtL
Δ-constructor-inductive-hypotheses : Δ x x t -> Ξ
[(Δ-constructor-inductive-hypotheses Δ x_D x_ci t_P)
(hypotheses-loop x_D t_P (Δ-constructor-inductive-telescope Δ x_D x_ci))])
(define-metafunction tt-ctxtL
Δ-constructor-method-telescope : Δ x x t -> Ξ
[(Δ-constructor-method-telescope Δ x_D x_ci t_P)
(Π (x_mi : (in-hole Ξ_a (in-hole Ξ_h ((in-hole Θ_p t_P) (Ξ-apply Ξ_a x_ci)))))
hole)
(where Θ_p (Δ-constructor-parameters Δ x_D x_ci))
(where Ξ_a (Δ-constructor-telescope Δ x_D x_ci))
(where Ξ_h (Δ-constructor-inductive-hypotheses Δ x_D x_ci t_P))
(where x_mi ,(variable-not-in (term (t_P Δ)) 'x-mi))])
;; fold Ξ-compose over map Δ-constructor-method-telescope over the list of constructors
(define-metafunction tt-ctxtL
method-loop : Δ x t (x ...) -> Ξ
[(method-loop Δ x_D t_P ()) hole]
[(method-loop Δ x_D t_P (x_0 x_rest ...))
(Ξ-compose (Δ-constructor-method-telescope Δ x_D x_0 t_P) (method-loop Δ x_D t_P (x_rest ...)))])
;; Returns the telescope of all methods required to eliminate the type x_D with motive t_P
(define-metafunction tt-ctxtL
Δ-methods-telescope : Δ x t -> Ξ
[(Δ-methods-telescope Δ x_D t_P)
(method-loop Δ x_D t_P (Δ-ref-constructors Δ x_D))])
;; Computes the type of the eliminator for the inductively defined type x_D with a motive whose result
;; is in universe U.
;;
@ -269,40 +325,42 @@
;; Ξ_P*D is the telescope of the parameters of x_D and
;; the witness of type x_D (applied to the parameters)
;; Ξ_m is the telescope of the methods for x_D
;; Returns the inductive hypotheses required for the elimination method of constructor c_i for
;; inductive type D, when eliminating with motive t_P.
(define-metafunction tt-ctxtL
Δ-constructor-inductive-hypotheses : Δ D c t -> Ξ
[(Δ-constructor-inductive-hypotheses Δ D c_i t_P)
(hypotheses-loop D t_P (Δ-constructor-inductive-telescope Δ D c_i))])
;; Returns the type of the method corresponding to c_i
(define-metafunction tt-ctxtL
Δ-constructor-method-type : Δ D c t -> t
[(Δ-constructor-method-type Δ D c_i t_P)
(in-hole Ξ_a (in-hole Ξ_h ((in-hole Θ_p t_P) (Ξ-apply Ξ_a c_i))))
(where Θ_p (Δ-constructor-parameters Δ D c_i))
(where Ξ_a (Δ-constructor-telescope Δ D c_i))
(where Ξ_h (Δ-constructor-inductive-hypotheses Δ D c_i t_P))])
(define-metafunction tt-ctxtL
Δ-method-types : Δ D e -> (t ...)
[(Δ-method-types Δ D e)
,(map (lambda (c) (term (Δ-constructor-method-type Δ D ,c e))) (term (c ...)))
(where (c ...) (Δ-ref-constructors Δ D))])
(define-metafunction tt-ctxtL
Δ-motive-type : Δ D U -> t
[(Δ-motive-type Δ D U)
(in-hole Ξ_P*D U)
(where Ξ (Δ-ref-parameter-Ξ Δ D))
Δ-elim-type : Δ x U -> t
[(Δ-elim-type Δ x_D U)
(Π (x_P : (in-hole Ξ_P*D U))
;; The methods Ξ_m for each constructor of type x_D
(in-hole Ξ_m
;; And finally, the parameters and discriminant
(in-hole Ξ_P*D
;; The result is (P a ... (x_D a ...)), i.e., the motive
;; applied to the paramters and discriminant
(Ξ-apply Ξ_P*D x_P))))
;; Get the parameters of x_D
(where Ξ (Δ-ref-parameter-Ξ Δ x_D))
;; A fresh name to bind the discriminant
(where x ,(variable-not-in (term (Δ D Ξ)) 'x-D))
(where x ,(variable-not-in (term (Δ Γ x_D Ξ)) 'x-D))
;; The telescope (∀ a -> ... -> (D a ...) hole), i.e.,
;; of the indices and the inductive type applied to the
;; indices
(where Ξ_P*D (in-hole Ξ (Π (x : (Ξ-apply Ξ D)) hole)))])
;; of the parameters and the inductive type applied to the
;; parameters
(where Ξ_P*D (in-hole Ξ (Π (x : (Ξ-apply Ξ x_D)) hole)))
;; A fresh name for the motive
(where x_P ,(variable-not-in (term (Δ Γ x_D Ξ Ξ_P*D x)) 'x-P))
;; The types of the methods for this inductive.
(where Ξ_m (Δ-methods-telescope Δ x_D x_P))])
;; TODO: This might belong in the next section, since it's related to evaluation
;; Generate recursive applications of the eliminator for each inductive argument of type x_D.
;; In more detaill, given motive t_P, parameters Θ_p, methods Θ_m, and arguments Θ_i to constructor
;; x_ci for x_D, for each inductively smaller term t_i of type (in-hole Θ_p x_D) inside Θ_i,
;; generate: (elim x_D U t_P Θ_m ... Θ_p ... t_i)
(define-metafunction tt-ctxtL
Δ-inductive-elim : Δ x U t Θ Θ Θ -> Θ
[(Δ-inductive-elim Δ x_D U t_P Θ_p Θ_m (in-hole Θ_i (hole (name t_i (in-hole Θ_r x_ci)))))
((Δ-inductive-elim Δ x_D U t_P Θ_p Θ_m Θ_i)
(in-hole ((in-hole Θ_p Θ_m) t_i) ((elim x_D U) t_P)))
(side-condition (memq (term x_ci) (term (Δ-ref-constructors Δ x_D))))]
[(Δ-inductive-elim Δ x_D U t_P Θ_p Θ_m Θ_nr) hole])
;;; ------------------------------------------------------------------------
;;; Dynamic semantics
@ -310,77 +368,55 @@
;;; inductively defined type x with a motive whose result is in universe U
(define-extended-language tt-redL tt-ctxtL
(v ::= x U (Π (x : t) t) (λ (x : t) t) (in-hole Θv c))
(Θv ::= hole (Θv v))
(C-elim ::= (elim D t_P (e_i ...) (e_m ...) hole))
;; call-by-value
(E ::= hole (E e) (v E)
(elim D e (e ...) (v ... E e ...) e)
(elim D e (e ...) (v ...) E)
;; reduce under Π (helps with typing checking)
;; TODO: Should be done in conversion judgment
(Π (x : v) E) (Π (x : E) e)))
;; NB: (in-hole Θv (elim x U)) is only a value when it's a partially applied elim. However,
;; determining whether or not it is partially applied cannot be done with the grammar alone.
(v ::= x U (Π (x : t) t) (λ (x : t) t) (elim x U) (in-hole Θv x) (in-hole Θv (elim x U)))
(Θv ::= hole (Θv v))
;; call-by-value, plus reduce under Π (helps with typing checking)
(E ::= hole (E e) (v E) (Π (x : v) E) (Π (x : E) e)))
(define Θv? (redex-match? tt-redL Θv))
(define E? (redex-match? tt-redL E))
(define v? (redex-match? tt-redL v))
#|
| The elim form must appear applied like so:
| (elim D U v_P m_0 ... m_i m_j ... m_n p ... (c_i a ...))
|
| Where:
| D is the inductive being eliminated
| U is the universe of the result of the motive
| v_P is the motive
| m_{0..n} are the methods
| p ... are the parameters of x_D
| c_i is a constructor of x_d
| a ... are the arguments to c_i
|
| Using contexts, this appears as (in-hole Θ ((elim D U) v_P))
|#
(define-metafunction tt-ctxtL
is-inductive-argument : Δ D t -> #t or #f
;; Think this only works in call-by-value. A better solution would
;; be to check position of the argument w.r.t. the current
;; method. requires more arguments, and more though.q
[(is-inductive-argument Δ D (in-hole Θ c_i))
,(and (memq (term c_i) (term (Δ-ref-constructors Δ D))) #t)])
;; Generate recursive applications of the eliminator for each inductive argument of type x_D.
;; In more detail, given motive t_P, parameters Θ_p, methods Θ_m, and arguments Θ_i to constructor
;; x_ci for x_D, for each inductively smaller term t_i of type (in-hole Θ_p x_D) inside Θ_i,
;; generate: (elim x_D U t_P Θ_m ... Θ_p ... t_i)
;; TODO TTEESSSSSTTTTTTTT
(define-metafunction tt-redL
Δ-inductive-elim : Δ D C-elim Θ -> Θ
;; NB: If metafunction fails, recursive
;; NB: elimination will be wrong. This will introduced extremely sublte bugs,
;; NB: inconsistency, failure of type safety, and other bad things.
;; NB: It should be tested and audited thoroughly
[(Δ-inductive-elim any ... hole)
hole]
[(Δ-inductive-elim Δ D C-elim (Θ_c t_i))
((Δ-inductive-elim Δ D C-elim Θ_c)
(in-hole C-elim t_i))
(side-condition (term (is-inductive-argument Δ D t_i)))]
[(Δ-inductive-elim any ... (Θ_c t_i))
(Δ-inductive-elim any ... Θ_c)])
(define tt-->
(reduction-relation tt-redL
(--> (Δ (in-hole E ((λ (x : t_0) t_1) t_2)))
(Δ (in-hole E (subst t_1 x t_2)))
-->β)
(--> (Δ (in-hole E (elim D e_motive (e_i ...) (v_m ...) (in-hole Θv_c c))))
(Δ (in-hole E (in-hole Θ_mi v_mi)))
;; Find the method for constructor c_i, relying on the order of the arguments.
(where natural (Δ-constructor-index Δ D c))
(where v_mi ,(list-ref (term (v_m ...)) (term natural)))
(--> (Δ (in-hole E (in-hole Θv ((elim x_D U) v_P))))
(Δ (in-hole E (in-hole Θ_r (in-hole Θv_i v_mi))))
#|
| The elim form must appear applied like so:
| (elim x_D U v_P m_0 ... m_i m_j ... m_n p ... (c_i a ...))
|
| Where:
| x_D is the inductive being eliminated
| U is the universe of the result of the motive
| v_P is the motive
| m_{0..n} are the methods
| p ... are the parameters of x_D
| c_i is a constructor of x_d
| a ... are the arguments to c_i
| Unfortunately, Θ contexts turn all this inside out:
| TODO: Write better abstractions for this notation
|#
;; Split Θv into its components: the paramters Θv_P for x_D, the methods Θv_m for x_D, and
;; the discriminant: the constructor x_ci applied to its argument Θv_i
(where (in-hole (Θv_p (in-hole Θv_i x_ci)) Θv_m) Θv)
;; Check that Θ_p actually matches the parameters of x_D, to ensure it doesn't capture other
;; arguments.
(side-condition (equal? (term (Θ-length Θv_p)) (term (Ξ-length (Δ-ref-parameter-Ξ Δ x_D)))))
;; Ensure x_ci is actually a constructor for x_D
(where (x_c* ...) (Δ-ref-constructors Δ x_D))
(side-condition (memq (term x_ci) (term (x_c* ...))))
;; There should be a number of methods equal to the number of constructors; to ensure E
;; doesn't capture methods and Θv_m doesn't capture other arguments
(side-condition (equal? (length (term (x_c* ...))) (term (Θ-length Θv_m))))
;; Find the method for constructor x_ci, relying on the order of the arguments.
(where v_mi (Θ-ref Θv_m (Δ-constructor-index Δ x_D x_ci)))
;; Generate the inductive recursion
(where Θ_ih (Δ-inductive-elim Δ D (elim D e_motive (e_i ...) (v_m ...) hole) Θv_c))
(where Θ_mi (in-hole Θ_ih Θv_c))
(where Θ_r (Δ-inductive-elim Δ x_D U v_P Θv_p Θv_m Θv_i))
-->elim)))
(define-metafunction tt-redL
@ -394,7 +430,23 @@
[(reduce Δ e)
e_r
(where (_ e_r)
,(car (apply-reduction-relation* tt--> (term (Δ e)) #:cache-all? #t)))])
,(let ([r (apply-reduction-relation* tt--> (term (Δ e)) #:cache-all? #t)])
;; Efficient check for (= (length r) 1)
;; NB: Check is overly aggressive and produces wrong error,
;; because not reducing under lambda.
#;(unless (null? (cdr r))
(error "Church-Rosser broken" r))
(car r)))])
(define-judgment-form tt-redL
#:mode (equivalent I I I)
#:contract (equivalent Δ t t)
[(where t_2 (reduce Δ t_0))
(where t_3 (reduce Δ t_1))
(side-condition (α-equivalent? t_2 t_3))
----------------- "≡-αβ"
(equivalent Δ t_0 t_1)])
;;; ------------------------------------------------------------------------
;;; Type checking and synthesis
@ -405,24 +457,6 @@
(Γ ::= (Γ x : t)))
(define Γ? (redex-match? tt-typingL Γ))
(define-judgment-form tt-typingL
#:mode (convert I I I I)
#:contract (convert Δ Γ t t)
[(side-condition ,(<= (term i_0) (term i_1)))
----------------- "≼-Unv"
(convert Δ Γ (Unv i_0) (Unv i_1))]
[(where t_2 (reduce Δ t_0))
(where t_3 (reduce Δ t_1))
(side-condition (α-equivalent? t_2 t_3))
----------------- "≼-αβ"
(convert Δ Γ t_0 t_1)]
[(convert Δ (Γ x : t_0) t_1 t_2)
----------------- "≼-Π"
(convert Δ Γ (Π (x : t_0) t_1) (Π (x : t_0) t_2))])
(define-metafunction tt-typingL
Γ-union : Γ Γ -> Γ
[(Γ-union Γ ) Γ]
@ -541,22 +575,16 @@
----------------- "DTR-Application"
(type-infer Δ Γ (e_0 e_1) t_3)]
[(type-check Δ Γ e_c (apply D e_i ...))
(type-infer Δ Γ e_motive (name t_motive (in-hole Ξ U)))
(convert Δ Γ t_motive (Δ-motive-type Δ D U))
(where (t_m ...) (Δ-method-types Δ D e_motive))
(type-check Δ Γ e_m t_m) ...
[(where t (Δ-elim-type Δ D U))
(type-infer Δ Γ t U_e)
----------------- "DTR-Elim_D"
(type-infer Δ Γ (elim D e_motive (e_i ...) (e_m ...) e_c)
(apply e_motive e_i ... e_c))])
(type-infer Δ Γ (elim D U) t)])
(define-judgment-form tt-typingL
#:mode (type-check I I I I)
#:contract (type-check Δ Γ e t)
[(type-infer Δ Γ e t_0)
(convert Δ Γ t t_0)
(equivalent Δ t t_0)
----------------- "DTR-Check"
(type-check Δ Γ e t)])

View File

@ -2,7 +2,7 @@
;; This module just provide module language sugar over the redex model.
(require
(except-in "redex-core.rkt" apply)
"redex-core.rkt"
redex/reduction-semantics
racket/provide-syntax
(for-syntax
@ -11,7 +11,7 @@
racket/syntax
(except-in racket/provide-transform export)
racket/require-transform
(except-in "redex-core.rkt" apply)
"redex-core.rkt"
redex/reduction-semantics))
(provide
;; Basic syntax
@ -30,10 +30,12 @@
[dep-provide provide]
[dep-require require]
[dep-lambda lambda]
[dep-lambda λ]
[dep-app #%app]
[dep-forall Π]
[dep-forall forall]
[dep-forall ]
[dep-inductive data]
@ -60,10 +62,10 @@
(all-from-out racket/syntax)
cur->datum
cur-expand
cur-type-infer
cur-type-check?
cur-normalize
cur-step
type-infer/syn
type-check/syn?
normalize/syn
step/syn
cur-equal?))
(begin-for-syntax
@ -177,11 +179,10 @@
[e (parameterize ([gamma (extend-Γ/term gamma x t)])
(cur->datum #'e))])
(term (,(syntax->datum #'b) (,x : ,t) ,e)))]
[(elim D motive (i ...) (m ...) d)
(term (elim ,(cur->datum #'D) ,(cur->datum #'motive)
,(map cur->datum (syntax->list #'(i ...)))
,(map cur->datum (syntax->list #'(m ...)))
,(cur->datum #'d)))]
[(elim t1 t2)
(let* ([t1 (cur->datum #'t1)]
[t2 (cur->datum #'t2)])
(term (elim ,t1 ,t2)))]
[(#%app e1 e2)
(term (,(cur->datum #'e1) ,(cur->datum #'e2)))]))))
(unless (or (inner-expand?) (type-infer/term reified-term))
@ -190,29 +191,35 @@
reified-term)
(define (datum->cur syn t)
(let datum->cur ([t t])
(match t
[(list (quote term) e)
(quasisyntax/loc syn
(datum->cur e))]
[(list (quote Unv) i)
(quasisyntax/loc syn
(Type #,i))]
[(list (quote Π) (list x (quote :) t) body)
(quasisyntax/loc syn
(dep-forall (#,(datum->syntax syn x) : #,(datum->cur t)) #,(datum->cur body)))]
[(list (quote λ) (list x (quote :) t) body)
(quasisyntax/loc syn
(dep-lambda (#,(datum->syntax syn x) : #,(datum->cur t)) #,(datum->cur body)))]
[(list (list (quote elim) t1) t2)
(quasisyntax/loc syn
(dep-elim #,(datum->cur t1) #,(datum->cur t2)))]
[(list e1 e2)
(quasisyntax/loc syn
(dep-app #,(datum->cur e1) #,(datum->cur e2)))]
[_
(quasisyntax/loc syn
#,(datum->syntax syn t))])))
(match t
[(list (quote term) e)
(quasisyntax/loc
syn
(datum->cur syn e))]
[(list (quote Unv) i)
(quasisyntax/loc
syn
(Type #,i))]
[(list (quote Π) (list x (quote :) t) body)
(quasisyntax/loc
syn
(dep-forall (#,(datum->syntax syn x) : #,(datum->cur syn t)) #,(datum->cur syn body)))]
[(list (quote λ) (list x (quote :) t) body)
(quasisyntax/loc
syn
(dep-lambda (#,(datum->syntax syn x) : #,(datum->cur syn t)) #,(datum->cur syn body)))]
[(list (list (quote elim) t1) t2)
(quasisyntax/loc
syn
(dep-elim #,(datum->cur syn t1) #,(datum->cur syn t2)))]
[(list e1 e2)
(quasisyntax/loc
syn
(dep-app #,(datum->cur syn e1) #,(datum->cur syn e2)))]
[_
(quasisyntax/loc
syn
#,(datum->syntax syn t))]))
(define (eval-cur syn)
(term (reduce ,(delta) ,(subst-bindings (cur->datum syn)))))
@ -225,29 +232,29 @@
;; Reflection tools
(define (cur-normalize syn)
(define (normalize/syn syn)
(datum->cur
syn
(eval-cur syn)))
(define (cur-step syn)
(define (step/syn syn)
(datum->cur
syn
(term (step ,(delta) ,(subst-bindings (cur->datum syn))))))
;; Are these two terms equivalent in type-systems internal equational reasoning?
(define (cur-equal? e1 e2)
(and (judgment-holds (convert ,(delta) ,(gamma) ,(eval-cur e1) ,(eval-cur e2))) #t))
(and (judgment-holds (equivalent ,(delta) ,(eval-cur e1) ,(eval-cur e2))) #t))
;; TODO: Document local-env
(define (cur-type-infer syn #:local-env [env '()])
(define (type-infer/syn syn #:local-env [env '()])
(parameterize ([gamma (for/fold ([gamma (gamma)])
([(x t) (in-dict env)])
(extend-Γ/syn (thunk gamma) x t))])
(let ([t (type-infer/term (eval-cur syn))])
(and t (datum->cur syn t)))))
(define (cur-type-check? syn type)
(define (type-check/syn? syn type)
(type-check/term? (eval-cur syn) (eval-cur type)))
;; Takes a Cur term syn and an arbitrary number of identifiers ls. The cur term is
@ -411,45 +418,42 @@
;;
;; TODO: Can these be simplified further?
(define-syntax (dep-lambda syn)
(syntax-parse syn
#:datum-literals (:)
[(_ (x:id : t) e)
(syntax-case syn (:)
[(_ (x : t) e)
(syntax->curnel-syntax
(quasisyntax/loc syn (λ (x : t) e)))]))
(define-syntax (dep-app syn)
(syntax-parse syn
[(_ e1:expr e2:expr)
(syntax-case syn ()
[(_ e1 e2)
(syntax->curnel-syntax
(quasisyntax/loc syn (#%app e1 e2)))]))
(define-syntax (dep-forall syn)
(syntax-parse syn
#:datum-literals (:)
[(_ (x:id : t) e)
(syntax-case syn (:)
[(_ (x : t) e)
(syntax->curnel-syntax
(quasisyntax/loc syn (Π (x : t) e)))]))
(define-syntax (Type syn)
(syntax-parse syn
[(_ i:nat)
(syntax-case syn ()
[(_ i)
(syntax->curnel-syntax
(quasisyntax/loc syn (Unv i)))]
[_ (quasisyntax/loc syn (Type 0))]))
(define-syntax (dep-inductive syn)
(syntax-parse syn
#:datum-literals (:)
[(_ i:id : ti (x1:id : t1) ...)
(syntax-case syn (:)
[(_ i : ti (x1 : t1) ...)
(begin
(extend-Δ/syn! delta #'i #'ti #'((x1 : t1) ...))
#'(void))]))
(define-syntax (dep-elim syn)
(syntax-parse syn
[(_ D:id motive (i ...) (m ...) e)
(syntax-case syn ()
[(_ D T)
(syntax->curnel-syntax
(quasisyntax/loc syn (elim D motive (i ...) (m ...) e)))]))
(quasisyntax/loc syn (elim D T)))]))
(define-syntax-rule (dep-void) (void))

View File

@ -1,2 +1,2 @@
#lang s-exp syntax/module-reader
cur
cur/cur

View File

@ -1,449 +1,314 @@
#lang s-exp "main.rkt"
#lang s-exp "cur.rkt"
;; Olly: The OTT-Like LibrarY
;; TODO: Automagically create a parser from bnf grammar
(require
"stdlib/sugar.rkt"
"stdlib/nat.rkt"
;; TODO: "real-"? More like "curnel-"
(only-in
"main.rkt"
[#%app real-app]
[elim real-elim]
[Π real-forall]
[λ real-lambda]))
(only-in "cur.rkt" [#%app real-app] [elim real-elim]))
(provide
define-relation
define-language
Var
avar
var-equal?
generate-coq
;; private; exported for testing only
(for-syntax
typeset-relation
typeset-bnf
cur->coq))
coq-defns
output-latex-bnf
output-coq
new-name
fresh-name))
(begin-for-syntax
(define-syntax-class dash
(pattern x:id
#:fail-unless (regexp-match #rx"-+" (symbol->string (syntax-e #'x)))
"Invalid dash"))
(define-syntax-class decl (pattern (x:id (~datum :) t:id)))
;; TODO: Automatically infer decl ... by binding all free identifiers?
;; TODO: Automatically infer decl ... for meta-variables that are the
;; same as bnf grammar.
(define-syntax-class inferrence-rule
(pattern (d:decl ...
x*:expr ...
line:dash lab:id
(name:id y* ...))
#:with rule #'(lab : (forall* d ...
(->* x* ... (name y* ...))))
;; TODO: convert meta-vars such as e1 to e_1
#:attr latex (format "\\inferrule~n{~a}~n{~a}"
(string-trim
(for/fold ([str ""])
([hyp (syntax->datum #'(x* ...))])
(format "~a~a \\+" str hyp))
" \\+"
#:left? #f)
(format "~a" (syntax->datum #'(name y* ...)))))))
(define-syntax (define-relation syn)
(syntax-parse syn
[(_ (n:id types* ...)
(~optional (~seq #:output-coq coq-file:str))
(~optional (~seq #:output-latex latex-file:str))
rules:inferrence-rule ...)
#:fail-unless (andmap (curry equal? (length (syntax->datum #'(types* ...))))
(map length (syntax->datum #'((rules.y* ...)
...))))
"Mismatch between relation declared and relation definition"
#:fail-unless (andmap (curry equal? (syntax->datum #'n))
(syntax->datum #'(rules.name ...)))
"Mismatch between relation declared name and result of inference rule"
(let ([output #`(data n : (->* types* ... Type) rules.rule ...)])
;; TODO: Pull this out into a separate function and test. Except
;; that might make using attritbutes more difficult.
(when (attribute latex-file)
(with-output-to-file (syntax->datum #'latex-file)
(thunk
(printf (format "\\fbox{$~a$}$~n$\\begin{mathpar}~n~a~n\\end{mathpar}$$"
(syntax->datum #'(n types* ...))
(string-trim
(for/fold ([str ""])
([rule (attribute rules.latex)])
(format "~a~a\\and~n" str rule))
"\\and"
#:left? #f))))
#:exists 'append))
#`(begin
#,@(if (attribute coq-file)
#`((generate-coq #:file coq-file #:exists
'append #,output))
#'())
#,output))]))
(begin-for-syntax
(require racket/syntax)
(define (new-name name . id*)
(apply format-id name (for/fold ([str "~a"])
([_ id*])
(string-append str "-~a")) name (map syntax->datum id*)))
(define (fresh-name id)
(datum->syntax id (gensym (syntax->datum id)))))
;; TODO: Oh, this is a mess. Rewrite it.
(begin-for-syntax
(define lang-name (make-parameter #'name))
(define nts (make-parameter (make-immutable-hash)))
(define-syntax-class nt
(pattern e:id #:fail-unless (hash-has-key? (nts) (syntax->datum #'e)) #f
#:attr name (hash-ref (nts) (syntax->datum #'e))
#:attr type (hash-ref (nts) (syntax->datum #'e))))
(define (flatten-args arg arg*)
(for/fold ([ls (syntax->list arg)])
([e (syntax->list arg*)])
(append ls (syntax->list e))))
(define-syntax-class (right-clause type)
#;(pattern (~datum var)
#:attr clause-context #`(#,(new-name (lang-name) #'var) :
(-> #,(hash-ref (nts) 'var) #,(hash-ref (nts) type)))
#:attr name #'var
#:attr arg-context #'(var))
(pattern e:nt
#:attr clause-context #`(#,(new-name #'e.name #'->
(hash-ref (nts) type)) :
(-> e.type #,(hash-ref (nts) type)))
#:attr name (fresh-name #'e.name)
#:attr arg-context #'(e.type))
(pattern x:id
#:attr clause-context #`(#,(new-name (lang-name) #'x) :
#,(hash-ref (nts) type))
#:attr name (new-name (lang-name) #'x)
#:attr arg-context #'())
(pattern ((~var e (right-clause type)) (~var e* (right-clause type)) ...)
#:attr name (fresh-name #'e.name)
#:attr clause-context #`(e.name : (->* #,@(flatten-args #'e.arg-context #'(e*.arg-context ...))
#,(hash-ref (nts) type)))
#:attr arg-context #`(#,@(flatten-args #'e.arg-context #'(e*.arg-context ...)))))
(define-syntax-class (right type)
(pattern ((~var r (right-clause type)) ...)
#:attr clause #'(r.clause-context ...)))
#;(define-syntax-class left
(pattern (type:id (nt*:id ...+))
#:do ))
(define-syntax-class nt-clauses
(pattern ((type:id (nt*:id ...+)
(~do (nts (for/fold ([ht (nts)])
([nt (syntax->datum #'(type nt* ...))])
(hash-set ht nt (new-name (lang-name) #'type)))))
(~datum ::=)
. (~var rhs* (right (syntax->datum #'type)))) ...)
#:with defs (with-syntax ([(name* ...)
(map (λ (x) (hash-ref (nts) x))
(syntax->datum #'(type ...)))])
#`((data name* : Type . rhs*.clause)
...)))))
(begin-for-syntax
;; TODO: More clever use of syntax-parse would enable something akin to what
;; define-relation is doing---having attributes that contain the latex
;; code for each clause.
;; TODO: convert meta-vars such as e1 to e_1
(define (output-latex-bnf vars clauses)
(format "$$\\begin{array}{lrrl}~n~a~n\\end{array}$$"
(for/fold ([str ""])
([clause (syntax->list clauses)])
(syntax-parse clause
#:datum-literals (::=)
[(type:id (nonterminal:id ...) ::= exprs ...)
(format "\\mbox{\\textit{~a}} & ~a & \\bnfdef & ~a\\\\~n"
(symbol->string (syntax->datum #'type))
(string-trim
(for/fold ([str ""])
([nt (syntax->datum #'(nonterminal ...))])
(format "~a~a," str nt))
","
#:left? #f)
(string-trim
(for/fold ([str ""])
([expr (syntax->datum #'(exprs ...))])
(format "~a~a \\bnfalt " str expr))
" \\bnfalt "
#:left? #f))]))))
(define (generate-latex-bnf file-name vars clauses)
(with-output-to-file file-name
(thunk (printf (output-latex-bnf vars clauses)))
#:exists 'append)))
;; TODO: For better error messages, add context, rename some of these patterns. e.g.
;; (type (meta-vars) ::= ?? )
;; TODO: Extend define-language with syntax such as ....
;; (term (e) ::= (e1 e2) ((lambda (x) e)
; #:latex "(\\lambda ,x. ,e)"))
(define-syntax (define-language syn)
(syntax-parse syn
[(_ name:id (~do (lang-name #'name))
(~do (nts (hash-set (make-immutable-hash) 'var #'Var)))
(~optional (~seq #:vars (x*:id ...)
(~do (nts (for/fold ([ht (nts)])
([v (syntax->datum #'(x* ...))])
(hash-set ht v (hash-ref ht 'var)))))))
(~optional (~seq #:output-coq coq-file:str))
(~optional (~seq #:output-latex latex-file:str))
. clause*:nt-clauses)
(let ([output #`(begin . clause*.defs)])
(when (attribute latex-file)
(generate-latex-bnf (syntax->datum #'latex-file) #'vars #'clause*))
#`(begin
#,@(if (attribute coq-file)
#`((generate-coq #:file coq-file #:exists 'append #,output))
#'())
#,output))]))
(data Var : Type (avar : (-> Nat Var)))
(define (var-equal? (v1 : Var) (v2 : Var))
(match v1
[(avar (n1 : Nat))
(match v2
[(avar (n2 : Nat))
(nat-equal? n1 n2)])]))
;; See stlc.rkt for examples
;; Generate Coq from Cur:
(begin-for-syntax
(define coq-defns (make-parameter ""))
(define (coq-lift-top-level str)
(coq-defns (format "~a~a~n" (coq-defns) str)))
;; TODO: OOps, type-infer doesn't return a cur term but a redex syntax bla
(define (constructor-args syn)
(syntax-parse (cur-type-infer syn)
(syntax-parse (type-infer/syn syn)
#:datum-literals (Π :)
[(Π (x:id : t) body)
(cons #'x (constructor-args #'body))]
[_ null]))
(define (sanitize-id str)
(let ([replace-by `((: _) (- _))])
(for/fold ([str str])
([p replace-by])
(string-replace str (symbol->string (first p))
(symbol->string (second p))))))
(define (cur->coq syn)
(parameterize ([coq-defns ""])
(define output
(let cur->coq ([syn syn])
(syntax-parse (cur-expand syn #'define #'begin)
;; TODO: Need to add these to a literal set and export it
;; Or, maybe overwrite syntax-parse
#:literals (real-lambda real-forall data real-app real-elim define begin Type)
[(begin e ...)
(for/fold ([str ""])
([e (syntax->list #'(e ...))])
(format "~a~n" (cur->coq e)))]
[(define name:id body)
(begin
(coq-lift-top-level
(format "Definition ~a := ~a.~n"
(cur->coq #'name)
(cur->coq #'body)))
"")]
[(define (name:id (x:id : t) ...) body)
(begin
(coq-lift-top-level
(format "Function ~a ~a := ~a.~n"
(cur->coq #'name)
(for/fold ([str ""])
([n (syntax->list #'(x ...))]
[t (syntax->list #'(t ...))])
(format "~a(~a : ~a) " str (cur->coq n) (cur->coq t)))
(cur->coq #'body)))
"")]
[(real-lambda ~! (x:id (~datum :) t) body:expr)
(format "(fun ~a : ~a => ~a)" (cur->coq #'x) (cur->coq #'t)
(cur->coq #'body))]
[(real-forall ~! (x:id (~datum :) t) body:expr)
(format "(forall ~a : ~a, ~a)" (syntax-e #'x) (cur->coq #'t)
(cur->coq #'body))]
[(data ~! n:id (~datum :) t (x*:id (~datum :) t*) ...)
(begin
(coq-lift-top-level
(format "Inductive ~a : ~a :=~a."
(sanitize-id (format "~a" (syntax-e #'n)))
(cur->coq #'t)
(for/fold ([strs ""])
([clause (syntax->list #'((x* : t*) ...))])
(syntax-parse clause
[(x (~datum :) t)
(format "~a~n| ~a : ~a" strs (syntax-e #'x)
(cur->coq #'t))]))))
"")]
[(Type i) "Type"]
[(real-elim var:id motive (i ...) (m ...) d)
(format
"(~a_rect ~a~a~a ~a)"
(cur->coq #'var)
(cur->coq #'motive)
(for/fold ([strs ""])
([m (syntax->list #'(m ...))])
(format "~a ~a" strs (cur->coq m)))
(for/fold ([strs ""])
([i (syntax->list #'(i ...))])
(format "~a ~a" strs (cur->coq i)))
(cur->coq #'d))]
[(real-app e1 e2)
(format "(~a ~a)" (cur->coq #'e1) (cur->coq #'e2))]
[e:id (sanitize-id (format "~a" (syntax->datum #'e)))])))
(format
"~a~a"
(coq-defns)
(if (regexp-match "^\\s*$" output)
""
(format "Eval compute in ~a." output))))))
(define (output-coq syn)
(syntax-parse (cur-expand syn #'define #'begin)
;; TODO: Need to add these to a literal set and export it
;; Or, maybe overwrite syntax-parse
#:literals (lambda forall data real-app real-elim define begin Type)
[(begin e ...)
(for/fold ([str ""])
([e (syntax->list #'(e ...))])
(format "~a~n" (output-coq e)))]
[(define name:id body)
(begin
(coq-lift-top-level
(format "Definition ~a := ~a.~n"
(output-coq #'name)
(output-coq #'body)))
"")]
[(define (name:id (x:id : t) ...) body)
(begin
(coq-lift-top-level
(format "Function ~a ~a := ~a.~n"
(output-coq #'name)
(for/fold ([str ""])
([n (syntax->list #'(x ...))]
[t (syntax->list #'(t ...))])
(format "~a(~a : ~a) " str (output-coq n) (output-coq t)))
(output-coq #'body)))
"")]
[(lambda ~! (x:id (~datum :) t) body:expr)
(format "(fun ~a : ~a => ~a)" (output-coq #'x) (output-coq #'t)
(output-coq #'body))]
[(forall ~! (x:id (~datum :) t) body:expr)
(format "(forall ~a : ~a, ~a)" (syntax-e #'x) (output-coq #'t)
(output-coq #'body))]
[(data ~! n:id (~datum :) t (x*:id (~datum :) t*) ...)
(begin
(coq-lift-top-level
(format "Inductive ~a : ~a :=~a."
(sanitize-id (format "~a" (syntax-e #'n)))
(output-coq #'t)
(for/fold ([strs ""])
([clause (syntax->list #'((x* : t*) ...))])
(syntax-parse clause
[(x (~datum :) t)
(format "~a~n| ~a : ~a" strs (syntax-e #'x)
(output-coq #'t))]))))
"")]
[(Type i) "Type"]
[(real-elim var t)
(format "~a_rect" (output-coq #'var))]
[(real-app e1 e2)
(format "(~a ~a)" (output-coq #'e1) (output-coq #'e2))]
[e:id (sanitize-id (format "~a" (syntax->datum #'e)))])))
(define-syntax (generate-coq syn)
(syntax-parse syn
[(_ (~optional (~seq #:file file))
(~optional (~seq #:exists flag))
body:expr)
(parameterize ([current-output-port
(if (attribute file)
(open-output-file
(syntax->datum #'file)
#:exists
(if (attribute flag)
;; TODO: AHH WHAT?
(eval (syntax->datum #'flag))
'error))
(current-output-port))])
(displayln (cur->coq #'body))
(~optional (~seq #:exists flag)) body:expr)
(parameterize ([current-output-port (if (attribute file)
(open-output-file (syntax->datum #'file)
#:exists
(if (attribute flag)
;; TODO: AHH WHAT?
(eval (syntax->datum #'flag))
'error))
(current-output-port))]
[coq-defns ""])
(define output
(let ([body (output-coq #'body)])
(if (regexp-match "^\\s*$" body)
""
(format "Eval compute in ~a." body))))
(displayln (format "~a~a" (coq-defns) output))
#'(begin))]))
;; TODO: Should these display or return a string?
(begin-for-syntax
(define (display-mathpartir)
(displayln
"%% Requires mathpartir, http://pauillac.inria.fr/~remy/latex/mathpartir.html")
(displayln
"%% or mttex, https://github.com/wilbowma/mttex")
(displayln
"\\usepackage{mathpartir}"))
(define (display-bnf)
(displayln
"%% Some auxillary defs. These should deleted if using mttex, https://github.com/wilbowma/mttex")
(displayln
"\\newcommand{\\bnfdef}{{\\bf ::=}}")
(displayln
"\\newcommand{\\bnfalt}{{\\bf \\mid}}")))
;; ------------------------------------
;; define-relation
(begin-for-syntax
(define-syntax-class horizontal-line
(pattern
x:id
#:when (regexp-match? #rx"-+" (symbol->string (syntax-e #'x)))))
(define-syntax-class hypothesis
(pattern (x:id (~datum :) t))
(pattern (~not e:horizontal-line)))
;; Alias syntax-classes with names for better error messages
(define-syntax-class rule-name
(pattern x:id))
(define-syntax-class relation-name
(pattern x:id))
(define-syntax-class relation-index
(pattern e:expr))
(define-syntax-class (conclusion n args lab)
(pattern
(name:id arg:expr ...)
#:attr rule-label-symbol (syntax-e lab)
#:attr rule-name-symbol (syntax-e #'name)
#:attr relation-name-symbol (syntax-e n)
#:fail-unless (eq? (attribute rule-name-symbol) (attribute relation-name-symbol))
(format "In rule ~a, name of conclusion ~a did not match name of relation ~a"
(attribute rule-label-symbol)
(attribute rule-name-symbol)
(attribute relation-name-symbol))
#:attr rule-arg-count (length (attribute arg))
#:attr relation-arg-count (length args)
#:fail-unless (= (attribute rule-arg-count) (attribute relation-arg-count))
(format "In rule ~a, conclusion applied to ~a arguments, while relation declared to have ~a arguments"
(attribute rule-label-symbol)
(attribute rule-arg-count)
(attribute relation-arg-count))))
;; TODO: Automatically infer hypotheses that are merely declarations by binding all free identifiers?
;; TODO: Automatically infer hypotheses as above for meta-variables that are the
;; same as bnf grammar, as a simple first case
(define-syntax-class (inferrence-rule name indices)
(pattern (h:hypothesis ...
#;line:horizontal-line
(~optional line:horizontal-line)
~!
lab:rule-name
(~var t (conclusion name indices (attribute lab))))
#:with constr-decl
#'(lab : (-> h ... (t.name t.arg ...)))
;; TODO: convert meta-vars such as e1 to e_1
#:attr latex
(format
"\\inferrule~n{~a}~n{~a}"
(string-trim
(for/fold ([str ""])
;; TODO: Perhaps omit hypotheses that are merely delcarations of free variables
([hyp (syntax->datum #'(h ...))])
(format "~a~a \\+" str hyp))
" \\+"
#:left? #f)
(format "~a" (syntax->datum #'(t.name t.arg ...))))))
;; TODO: Should this display or return a string?
(define (typeset-relation form rules-latex)
(display-mathpartir)
(printf
"\\fbox{$~a$}$~n$\\begin{mathpar}~n~a~n\\end{mathpar}"
form
(string-trim
(for/fold ([str ""])
([rule rules-latex])
(format "~a~a\\and~n" str rule))
"\\and"
#:left? #f))))
(define-syntax (define-relation syn)
(syntax-parse syn
[(_ (name:relation-name index:relation-index ...)
(~optional (~seq #:output-coq coq-file:str))
(~optional (~seq #:output-latex latex-file:str))
(~var rule (inferrence-rule (attribute name) (attribute index))) ...)
(let ([output #`(data name : (-> index ... Type) rule.constr-decl ...)])
(when (attribute latex-file)
(with-output-to-file (syntax->datum #'latex-file)
(thunk
(typeset-relation
(syntax->datum #'(name index ...))
(attribute rule.latex)))
#:exists 'append))
(when (attribute coq-file)
(with-output-to-file (syntax->datum #'coq-file)
(thunk (displayln (cur->coq output)))
#:exists 'append))
output)]))
;; ------------------------------------
;; define-language
(begin-for-syntax
;; A mutable dictionary from non-terminal meta-variables names to their types.
(define mv-map (make-parameter #f))
;; A set containing the meta-variables that represent variables.
(define vars (make-parameter #f))
;; The language name for the language currently being parsed
(define lang-name (make-parameter #f))
;; A meta-variable is any identifiers that belongs to the mv-map
(define-syntax-class meta-variable
(pattern
x:id
#:attr sym (syntax->datum #'x)
#:fail-unless (dict-has-key? (mv-map) (attribute sym)) #f
#:attr type (dict-ref (mv-map) (attribute sym))))
;; A var-meta-variable is a meta-variable that is declared to be
;; treated as a variable in the defined language.
(define-syntax-class var-meta-variable
(pattern
x:id
#:fail-unless (set-member? (vars) (syntax->datum #'x)) #f))
;; A terminal is a idnetifiers that is not a meta-variable. A terminal will always represent a constructor.
(define-syntax-class terminal
(pattern
x:id
#:attr sym (syntax->datum #'x)
#:fail-when (dict-has-key? (mv-map) (attribute sym)) #f
#:attr constructor-name
(format-id #'x "~a-~a" (lang-name) #'x)))
;; A terminal-args can appear as the argument to a terminal in
;; an expression, or as a sub-expression in a terminal-args.
;; Each terminal-args export args, a list of types the
;; terminal-args represents and the list of types the non-terminal's
;; constructor expects in this case.
(define-syntax-class (terminal-args non-terminal-type)
;; A meta-variable is a terminal-args
(pattern
e:meta-variable
#:attr args
(list #'e.type)
#:attr latex
(format "~a" (syntax-e #'e)))
;; An identifier is a terminal-args, but is treated as syntax
(pattern
x:id
#:attr args
'()
#:attr latex
(format "~a" (syntax-e #'x)))
;; So is an empty list
(pattern
()
#:attr args
'()
#:attr latex
"")
;; We use De-Bruijn indices, so binding positions are removed.
(pattern
(#:bind x:var-meta-variable . (~var t (terminal-args non-terminal-type)))
#:attr args
(attribute t.args)
#:attr latex
(format "~a ~a" (syntax-e #'x) (attribute t.latex)))
;; A terminal-args applied to other nested expressions is a terminal-args
(pattern
((~var h (terminal-args non-terminal-type))
(~var t (terminal-args non-terminal-type)) ...)
#:attr args
(for/fold ([ls (attribute h.args)])
([args (attribute t.args)])
(append ls args))
#:attr latex
(format "~a ~a" (attribute h.latex) (apply string-append (attribute t.latex)))))
;; a expression is parameterized by the name of the non-terminal to
;; which is belongs,
;; Each expression exports a constr-decl, which declares a
;; constructor for the non-terminal type.
(define-syntax-class (expression non-terminal-type)
;; A meta-variable is a valid expression.
;; Generates a conversion constructor in constr-decl, and the type of
(pattern
e:meta-variable
#:attr constructor-name
(format-id #'e "~a->~a" #'e.type non-terminal-type)
#:attr constr-decl
#`(constructor-name : (-> e.type #,non-terminal-type))
#:attr latex
(format "~a" (syntax-e #'e)))
;; An identifier is a valid expression, generating a base constructor.
(pattern
x:terminal
#:attr constr-decl
#`(x.constructor-name : #,non-terminal-type)
#:attr latex
(format "~a" (syntax-e #'x)))
;; A terminal applied to a terminal-args is a valid expression.
(pattern
(x:terminal . (~var c (terminal-args non-terminal-type)))
#:attr constr-decl
#`(x.constructor-name : (-> #,@(attribute c.args) #,non-terminal-type))
#:attr latex
(format "(~a ~a)" (syntax-e #'x) (attribute c.latex))))
(define-syntax-class non-terminal-def
(pattern
(name:id
(meta-var:id ...+)
(~optional (~datum ::=))
;; Create a name for the type of this non-terminal, from the
;; language name and the non-terminal name.
(~bind [nt-type (format-id #'name "~a-~a" (lang-name) #'name)])
;; Imperatively update the map from meta-variables to the
;; nt-type, to be used when generating the types of the constructors
;; for this and later non-terminal.
(~do (for ([mv (syntax->datum #'(meta-var ...))])
(dict-set! (mv-map) mv (attribute nt-type))))
(~var c (expression (attribute nt-type))) ...)
;; Generates the inductive data type for this non-terminal definition.
#:attr def
#`(data nt-type : Type c.constr-decl ...)
#:attr latex
(format
"\\mbox{\\textit{~a}} & ~a & \\bnfdef & ~a\\\\~n"
(symbol->string (syntax->datum #'name))
(string-trim
(for/fold ([str ""])
([nt (syntax->datum #'(meta-var ...))])
(format "~a~a," str nt))
","
#:left? #f)
(string-trim
(for/fold ([str ""])
([expr (attribute c.latex)])
(format "~a~a \\bnfalt " str expr))
" \\bnfalt "
#:left? #f))))
;; TODO: Should this display or return a string?
(define (typeset-bnf nt-latex)
(display-mathpartir)
(display-bnf)
(printf
"\begin{displaymath}~n\\begin{array}{lrrl}~n~a~n\\end{array}~n\end{displaymath}"
(apply string-append nt-latex))))
;; TODO: For better error messages, add context
;; TODO: Extend define-language with syntax such as ....
;; (term (e) ::= (e1 e2) ((lambda (x) e)
(define-syntax (define-language syn)
(define/syntax-parse
(_ name:id
(~optional (~seq #:vars (x:id ...)))
(~optional (~seq #:output-coq coq-file:str))
(~optional (~seq #:output-latex latex-file:str))
.
non-terminal-defs)
syn)
(parameterize ([mv-map (make-hash)]
[lang-name #'name]
[vars (apply set (map syntax->datum (or (attribute x) '())))])
(cond
[(attribute x) =>
(lambda (xls)
(for ([x xls])
(dict-set! (mv-map) (syntax-e x) #'Nat)))])
(syntax-parse #'non-terminal-defs
[(def:non-terminal-def ...)
(let ([output #`(begin def.def ...)])
(when (attribute latex-file)
(with-output-to-file (syntax-e #'latex-file)
(thunk (typeset-bnf (attribute def.latex)))
#:exists 'append))
(when (attribute coq-file)
(with-output-to-file (syntax-e #'coq-file)
(thunk (displayln (cur->coq output)))
#:exists 'append))
output)])))
;; See stlc.rkt for examples

View File

@ -1,4 +1,4 @@
#lang s-exp "../main.rkt"
#lang s-exp "../cur.rkt"
(require "sugar.rkt")
(provide Bool true false if not and or)

View File

@ -1,33 +0,0 @@
#lang s-exp "../main.rkt"
(require
"nat.rkt"
"maybe.rkt"
"sugar.rkt")
(provide
List
nil
cons
list-ref
length)
(data List : (-> (A : Type) Type)
(nil : (-> (A : Type) (List A)))
(cons : (-> (A : Type) A (List A) (List A))))
(define (list-ref (A : Type) (ls : (List A)))
(match ls
[(nil (A : Type)) (lambda (n : Nat) (none A))]
[(cons (A : Type) (a : A) (rest : (List A)))
(lambda (n : Nat)
(match n
[z (some A a)]
[(s (n-1 : Nat))
((recur rest) n-1)]))]))
(define (length (A : Type) (ls : (List A)))
(match ls
[(nil (A : Type))
z]
[(cons (A : Type) (a : A) (rest : (List A)))
(s (recur rest))]))

View File

@ -1,13 +1,13 @@
#lang s-exp "../main.rkt"
#lang s-exp "../cur.rkt"
(require "sugar.rkt")
(provide Maybe none some some/i)
(data Maybe : (forall (A : Type) Type)
(none : (forall (A : Type) (Maybe A)))
(some : (forall (A : Type) (a : A) (Maybe A))))
(some : (forall* (A : Type) (a : A) (Maybe A))))
(define-syntax (some/i syn)
(syntax-case syn ()
[(_ a)
(let ([a-ty (cur-type-infer #'a)])
(let ([a-ty (type-infer/syn #'a)])
#`(some #,a-ty a))]))

View File

@ -1,4 +1,4 @@
#lang s-exp "../main.rkt"
#lang s-exp "../cur.rkt"
(require "sugar.rkt" "bool.rkt")
;; TODO: override (all-defined-out) to enable exporting all these
;; properly.
@ -35,21 +35,17 @@
(define square (run (exp (s (s z)))))
(define (zero? (n : Nat))
(match n
[z true]
[(s (n : Nat))
false]))
(define (nat-equal? (n : Nat))
(match n
[z zero?]
[(s (n-1 : Nat))
(lambda (m : Nat)
(match m
[z false]
[(s (m-1 : Nat))
((recur n-1) m-1)]))]))
;; Credit to this function goes to Max
(define nat-equal?
(elim Nat Type (lambda (x : Nat) (-> Nat Bool))
(elim Nat Type (lambda (x : Nat) Bool)
true
(lambda* (x : Nat) (ih-n2 : Bool) false))
(lambda* (x : Nat) (ih : (-> Nat Bool))
(elim Nat Type (lambda (x : Nat) Bool)
false
(lambda* (x : Nat) (ih-bla : Bool)
(ih x))))))
(define (even? (n : Nat))
(match n

View File

@ -1,4 +1,4 @@
#lang s-exp "../main.rkt"
#lang s-exp "../cur.rkt"
(require "sugar.rkt")
;; TODO: Handle multiple provide forms properly
;; TODO: Handle (all-defined-out) properly
@ -24,62 +24,64 @@
(define-type (Not (A : Type)) (-> A False))
(data And : (forall (A : Type) (B : Type) Type)
(conj : (forall (A : Type) (B : Type)
(data And : (forall* (A : Type) (B : Type) Type)
(conj : (forall* (A : Type) (B : Type)
(x : A) (y : B) (And A B))))
(define-syntax (conj/i syn)
(syntax-case syn ()
[(_ a b)
(let ([a-type (cur-type-infer #'a)]
[b-type (cur-type-infer #'b)])
(let ([a-type (type-infer/syn #'a)]
[b-type (type-infer/syn #'b)])
#`(conj #,a-type #,b-type a b))]))
(define thm:and-is-symmetric
(forall (P : Type) (Q : Type) (ab : (And P Q)) (And Q P)))
(forall* (P : Type) (Q : Type) (ab : (And P Q)) (And Q P)))
(define pf:and-is-symmetric
(lambda (P : Type) (Q : Type) (ab : (And P Q))
(match ab
[(conj (P : Type) (Q : Type) (x : P) (y : Q))
(conj/i y x)])))
(lambda* (P : Type) (Q : Type) (ab : (And P Q))
(case* And Type ab (P Q)
(lambda* (P : Type) (Q : Type) (ab : (And P Q))
(And Q P))
((conj (P : Type) (Q : Type) (x : P) (y : Q)) IH: () (conj/i y x)))))
(define thm:proj1
(forall (A : Type) (B : Type) (c : (And A B)) A))
(forall* (A : Type) (B : Type) (c : (And A B)) A))
(define pf:proj1
(lambda (A : Type) (B : Type) (c : (And A B))
(match c
[(conj (A : Type) (B : Type) (a : A) (b : B)) a])))
(lambda* (A : Type) (B : Type) (c : (And A B))
(case* And Type c (A B)
(lambda* (A : Type) (B : Type) (c : (And A B)) A)
((conj (A : Type) (B : Type) (a : A) (b : B)) IH: () a))))
(define thm:proj2
(forall (A : Type) (B : Type) (c : (And A B)) B))
(forall* (A : Type) (B : Type) (c : (And A B)) B))
(define pf:proj2
(lambda (A : Type) (B : Type) (c : (And A B))
(match c
[(conj (A : Type) (B : Type) (a : A) (b : B)) b])))
(lambda* (A : Type) (B : Type) (c : (And A B))
(case* And Type c (A B)
(lambda* (A : Type) (B : Type) (c : (And A B)) B)
((conj (A : Type) (B : Type) (a : A) (b : B)) IH: () b))))
#| TODO: Disabled until #22 fixed
(data Or : (forall (A : Type) (B : Type) Type)
(left : (forall (A : Type) (B : Type) (a : A) (Or A B)))
(right : (forall (A : Type) (B : Type) (b : B) (Or A B))))
(data Or : (forall* (A : Type) (B : Type) Type)
(left : (forall* (A : Type) (B : Type) (a : A) (Or A B)))
(right : (forall* (A : Type) (B : Type) (b : B) (Or A B))))
(define-theorem thm:A-or-A
(forall (A : Type) (o : (Or A A)) A))
(forall* (A : Type) (o : (Or A A)) A))
(define proof:A-or-A
(lambda (A : Type) (c : (Or A A))
(lambda* (A : Type) (c : (Or A A))
;; TODO: What should the motive be?
(elim Or (lambda (A : Type) (B : Type) (c : (Or A B)) A)
(A A)
((lambda (A : Type) (B : Type) (a : A) a)
;; TODO: How do we know B is A?
(lambda (A : Type) (B : Type) (b : B) b))
c)))
(elim Or Type (lambda* (A : Type) (B : Type) (c : (Or A B)) A)
(lambda* (A : Type) (B : Type) (a : A) a)
;; TODO: How do we know B is A?
(lambda* (A : Type) (B : Type) (b : B) b)
A A c)))
(qed thm:A-or-A proof:A-or-A)
|#
(data == : (forall (A : Type) (x : A) (-> A Type))
(refl : (forall (A : Type) (x : A) (== A x x))))
(data == : (forall* (A : Type) (x : A) (-> A Type))
(refl : (forall* (A : Type) (x : A) (== A x x))))

View File

@ -1,18 +1,20 @@
#lang s-exp "../main.rkt"
#lang s-exp "../cur.rkt"
(provide
->
lambda
->*
forall*
lambda*
(rename-out
[-> ]
[-> forall]
[-> ]
[-> Π]
[-> Pi]
[lambda λ])
[->* →*]
[lambda* λ*]
[forall* ∀*])
#%app
define
:
elim
define-type
case
case*
match
recur
let
@ -27,353 +29,130 @@
query-type)
(require
(only-in "../main.rkt"
(only-in "../cur.rkt"
[elim real-elim]
[#%app real-app]
[λ real-lambda]
[Π real-Π]
[define real-define]))
(begin-for-syntax
(define-syntax-class result-type
(pattern type:expr))
(define-syntax-class parameter-declaration
(pattern (name:id (~datum :) type:expr))
(pattern
type:expr
#:attr name (format-id #'type "~a" (gensym 'anon-parameter)))))
;; A multi-arity function type; takes parameter declaration of either
;; a binding (name : type), or type whose name is generated.
;; E.g.
;; (-> (A : Type) A A)
(define-syntax (-> syn)
(syntax-parse syn
[(_ d:parameter-declaration ...+ result:result-type)
(foldr (lambda (src name type r)
(quasisyntax/loc src
(real-Π (#,name : #,type) #,r)))
#'result
(attribute d)
(attribute d.name)
(attribute d.type))]))
(syntax-case syn ()
[(_ t1 t2) #`(forall (#,(gensym) : t1) t2)]))
;; TODO: Add forall macro that allows specifying *names*, with types
;; inferred. unlike -> which require types but not names
;; E.g.
;; (forall x (y : Nat) (== Nat x y))
(define-syntax ->*
(syntax-rules ()
[(->* a) a]
[(->* a a* ...)
(-> a (->* a* ...))]))
;; TODO: Allows argument-declarations to have types inferred, similar
;; to above TODO forall
(begin-for-syntax
;; eta-expand syntax-class for error messages
(define-syntax-class argument-declaration
(pattern
e:parameter-declaration
#:attr name #'e.name
#:attr type #'e.type)))
(define-syntax (lambda syn)
(syntax-parse syn
[(_ d:argument-declaration ...+ body:expr)
(foldr (lambda (src name type r)
(quasisyntax/loc src
(real-lambda (#,name : #,type) #,r)))
#'body
(attribute d)
(attribute d.name)
(attribute d.type))]))
(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]))
;; TODO: This makes for really bad error messages when an identifier is undefined.
(define-syntax (#%app syn)
(syntax-case syn ()
[(_ e)
(quasisyntax/loc syn e)]
[(_ e) #'e]
[(_ e1 e2)
(quasisyntax/loc syn
(real-app e1 e2))]
#'(real-app e1 e2)]
[(_ e1 e2 e3 ...)
(quasisyntax/loc syn
(#%app (#%app e1 e2) e3 ...))]))
#'(#%app (#%app e1 e2) e3 ...)]))
(define-syntax define-type
(syntax-rules ()
[(_ (name (a : t) ...) body)
(define name (-> (a : t) ... body))]
(define name (forall* (a : t) ... body))]
[(_ name type)
(define name type)]))
;; Cooperates with define to allow Haskell-esque type annotations
#| TODO NB:
| This method of cooperating macros is sort of a terrible
| hack. Instead, need principled way of adding/retrieving information
| to/from current module. E.g. perhaps provide extensions an interface to
| module's term environment and inductive signature. Then, :: could add
| new "id : type" to environment, and define could extract type and use.
|#
(begin-for-syntax
(define annotation-dict (make-hash))
(define (annotation->types type-syn)
(let loop ([ls '()]
[syn type-syn])
(syntax-parse (cur-expand syn)
#:datum-literals (:)
[(real-Π (x:id : type) body)
(loop (cons #'type ls) #'body)]
[_ (reverse ls)]))))
(define-syntax (: syn)
(syntax-parse syn
[(_ name:id type:expr)
;; NB: Unhygenic; need to reuse Racket's identifiers, and make this type annotation a syntax property
(syntax-parse (cur-expand #'type)
#:datum-literals (:)
[(real-Π (x:id : type) body) (void)]
[_
(raise-syntax-error
':
"Can only declare annotations for functions, but not a function type"
syn)])
(dict-set! annotation-dict (syntax->datum #'name) (annotation->types #'type))
#'(void)]))
;; TODO: Allow inferring types as in above TODOs for lambda, forall
(define-syntax (define syn)
(syntax-parse syn
#:datum-literals (:)
[(define (name:id x:id ...) body)
(cond
[(dict-ref annotation-dict (syntax->datum #'name)) =>
(lambda (anns)
(quasisyntax/loc syn
(real-define name (lambda #,@(for/list ([x (syntax->list #'(x ...))]
[type anns])
#`(#,x : #,type)) body))))]
[else
(raise-syntax-error
'define
"Cannot omit type annotations unless you have declared them with (: name type) form first."
syn)])]
(syntax-case syn ()
[(define (name (x : t) ...) body)
(quasisyntax/loc syn
(real-define name (lambda (x : t) ... body)))]
#'(real-define name (lambda* (x : t) ... body))]
[(define id body)
(quasisyntax/loc syn
(real-define id body))]))
#'(real-define id body)]))
(define-syntax-rule (elim t1 t2 e ...)
((real-elim t1 t2) e ...))
#|
(begin-for-syntax
(define (type->telescope syn)
(syntax-parse (cur-expand syn)
#:literals (real-Π)
#:datum-literals (:)
[(real-Π (x:id : t) body)
(cons #'(x : t) (type->telescope #'body))]
[_ '()]))
(define (rewrite-clause clause)
(syntax-case clause (: IH:)
[((con (a : A) ...) IH: ((x : t) ...) body)
#'(lambda* (a : A) ... (x : t) ... body)]
[(e body) #'body])))
(define (type->body syn)
(syntax-parse syn
#:literals (real-Π)
#:datum-literals (:)
[(real-Π (x:id : t) body)
(type->body #'body)]
[e #'e]))
;; TODO: Expects clauses in same order as constructors as specified when
;; TODO: inductive D is defined.
;; TODO: Assumes D has no parameters
(define-syntax (case syn)
;; duplicated code
(define (clause-body syn)
(syntax-case (car (syntax->list syn)) (: IH:)
[((con (a : A) ...) IH: ((x : t) ...) body) #'body]
[(e body) #'body]))
(syntax-case syn (=>)
[(_ e clause* ...)
(let* ([D (type-infer/syn #'e)]
[M (type-infer/syn (clause-body #'(clause* ...)))]
[U (type-infer/syn M)])
#`(elim #,D #,U (lambda (x : #,D) #,M) #,@(map rewrite-clause (syntax->list #'(clause* ...)))
e))]))
(define (constructor-indices D syn)
(let loop ([syn syn]
[args '()])
(syntax-parse (cur-expand syn)
#:literals (real-app)
[D:id args]
[(real-app e1 e2)
(loop #'e1 (cons #'e2 args))])))
(define-syntax (case* syn)
(syntax-case syn ()
[(_ D U e (p ...) P clause* ...)
#`(elim D U P #,@(map rewrite-clause (syntax->list #'(clause* ...))) p ... e)]))
(define (inductive-index-telescope D)
(type->telescope (cur-type-infer D)))
(define (inductive-method-telescope D motive)
(for/list ([syn (cur-constructor-map D)])
(with-syntax ([(c : t) syn]
[name (gensym (format-symbol "~a-~a" #'c 'method))]
[((arg : arg-type) ...) (type->telescope #'t)]
[((rarg : rarg-type) ...) (constructor-recursive-args D #'((arg : arg-type) ...))]
[((ih : ih-type) ...) (constructor-inductive-hypotheses #'((rarg : rarg-type) ...) motive)]
[(iarg ...) (constructor-indices D (type->body #'t))]
)
#`(name : (forall (arg : arg-type) ...
(ih : ih-type) ...
(motive iarg ...)))))))
(define-syntax (elim syn)
(syntax-parse syn
[(elim D:id U e ...)
(with-syntax ([((x : t) ...) (inductive-index-telescope #'D)]
[motive (gensym 'motive)]
[y (gensym 'y)]
[disc (gensym 'disc)]
[((method : method-type) ...) (inductive-method-telescope #'D #'motive)])
#`((lambda
(motive : (forall (x : t) ... (y : (D x ...)) U))
(method : ) ...
(x : t) ...
(disc : (D x ...)) ...
(real-elim D motive (x ...) (method ...) disc))
e ...)
)
]))
|#
;; Quite fragie to give a syntactic treatment of pattern matching -> eliminator. Replace with "Elimination with a Motive"
(begin-for-syntax
(define-struct clause (args types decl body))
(define ih-dict (make-hash))
(define (clause-parse syn)
(syntax-case syn (:)
[((con (a : A) ...) body)
(make-clause (syntax->list #'(a ...)) (syntax->list #'(A ...)) #'((a : A) ...) #'body)]
[(e body)
(make-clause '() '() #'() #'body)]))
(define-syntax-class curried-application
(pattern
((~literal real-app) name:id e:expr)
#:attr args
(list #'e))
(define (infer-result clauses)
(or
(for/or ([clause clauses])
(type-infer/syn
(clause-body clause)
#:local-env (for/fold ([d '()])
([arg (clause-args clause)]
[type (clause-types clause)])
(dict-set d arg type))))
(raise-syntax-error
'match
"Could not infer type of result."
(clause-body (car clauses)))))
(pattern
((~literal real-app) a:curried-application e:expr)
#:attr name #'a.name
#:attr args
;; TODO BUG: repeated appends are not performant; cons then reverse in inductive-type-declaration
(append
(attribute a.args)
(list #'e))))
(define (infer-ihs D motive args types)
(for/fold ([ih-dict (make-immutable-hash)])
([type-syn types]
[arg-syn args]
;; NB: Non-hygenic
#:when (cur-equal? type-syn D))
(dict-set ih-dict (syntax->datum arg-syn) `(,(format-id arg-syn "ih-~a" arg-syn) . ,#`(#,motive #,arg-syn)))))
(define-syntax-class inductive-type-declaration
(pattern
x:id
#:attr inductive-name
#'x
#:attr indices
'()
#:attr decls
(list #`(#,(gensym 'anon-discriminant) : x))
#:attr abstract-indices
values)
(pattern
;; BUG TODO NB: Because the inductive type may have been inferred, it may appear in Curnel syntax, i.e., nested applications starting with dep-app
;; Best to ensure it *always* is in Curnel form, and pattern match on that.
a:curried-application
#:attr inductive-name
(attribute a.name)
#:attr indices
(attribute a.args)
#:attr names
(for/list ([e (attribute indices)])
(format-id e "~a" (gensym 'anon-index)))
#:attr types
;; TODO: Detect failure, report error/suggestions
(for/list ([e (attribute indices)])
(or (cur-type-infer e)
(raise-syntax-error
'match
(format
"Could not infer type of index ~a"
e)
e)))
#:attr decls
(append
(for/list ([name (attribute names)]
[type (attribute types)]
[src (attribute indices)])
(quasisyntax/loc src
(#,name : #,type)))
(list
(quasisyntax/loc #'a
(#,(gensym 'anon-discriminant) : (inductive-name #,@(attribute names))))))
#:attr abstract-indices
(lambda (return)
;; NB: unhygenic
;; Normalize at compile-time, for efficiency at run-time
(cur-normalize
#`((lambda
;; TODO: utteraly fragile; relines on the indices being referred to by name, not computed
;; works only for simple type familes and simply matches on them
#,@(for/list ([name (attribute indices)]
[type (attribute types)])
#`(#,name : #,type))
#,return)
#,@(attribute names))))))
;; todo: Support just names, inferring types
(define-syntax-class match-declaration
(pattern
;; TODO: Use parameter-declaration defined earlier
(name:id (~datum :) type:expr)
#:attr decl
#'(name : type)))
(define-syntax-class match-prepattern
;; TODO: Check that x is a valid constructor for the inductive type
(pattern
x:id
#:attr local-env
'()
#:attr decls
'()
#:attr types
'()
#:attr names
'())
(pattern
(x:id d:match-declaration ...+)
#:attr local-env
(for/fold ([d (make-immutable-hash)])
([name (attribute d.name)]
[type (attribute d.type)])
(dict-set d name type))
#:attr decls
(attribute d.decl)
#:attr names
(attribute d.name)
#:attr types
(attribute d.type)))
(define-syntax-class (match-pattern D motive)
(pattern
d:match-prepattern
#:attr decls
;; Infer the inductive hypotheses, add them to the pattern decls
;; and update the dictionarty for the recur form
(for/fold ([decls (attribute d.decls)])
([type-syn (attribute d.types)]
[name-syn (attribute d.names)]
[src (attribute d.decls)]
;; NB: Non-hygenic
;; BUG TODO: This fails when D is an inductive applied to arguments...
#:when (cur-equal? type-syn D))
(define/syntax-parse type:inductive-type-declaration (cur-expand type-syn))
(let ([ih-name (quasisyntax/loc src #,(format-id name-syn "ih-~a" name-syn))]
;; Normalize at compile-time, for efficiency at run-time
[ih-type (cur-normalize #`(#,motive #,@(attribute type.indices) #,name-syn))])
(dict-set! ih-dict (syntax->datum name-syn) ih-name)
(append decls (list #`(#,ih-name : #,ih-type)))))))
(define-syntax-class (match-preclause maybe-return-type)
(pattern
(p:match-prepattern b:expr)
#:attr return-type
;; TODO: Check that the infered type matches maybe-return-type, if it is provied
(or maybe-return-type
;; Ignore errors when trying to infer this type; other attempt might succeed
(with-handlers ([values (lambda _ #f)])
(cur-type-infer #:local-env (attribute p.local-env) #'b)))))
(define-syntax-class (match-clause D motive)
(pattern
((~var p (match-pattern D motive))
;; TODO: nothing more advanced?
b:expr)
#:attr method
(quasisyntax/loc #'p
#,(if (null? (attribute p.decls))
#'b
#`(lambda #,@(attribute p.decls) b))))))
(define (clause->method D motive clause)
(dict-clear! ih-dict)
(let* ([ihs (infer-ihs D motive (clause-args clause) (clause-types clause))]
[ih-args (dict-map
ihs
(lambda (k v)
(dict-set! ih-dict k (car v))
#`(#,(car v) : #,(cdr v))))])
#`(lambda* #,@(clause-decl clause) #,@ih-args #,(clause-body clause)))))
(define-syntax (recur syn)
(syntax-case syn ()
@ -384,49 +163,23 @@
(lambda ()
(raise-syntax-error
'match
;; TODO: Detect when inside a match to provide better error
(format
"Cannot recur on ~a. Ether not inside a match or ~a is not an inductive argument."
(syntax->datum #'id)
(syntax->datum #'id))
(format "Cannot recur on ~a" (syntax->datum #'id))
syn)))]))
;; TODO: Test
(define-syntax (match syn)
(syntax-parse syn
[(_ d
~!
(~optional
(~seq #:in ~! t)
#:defaults
([t (or (cur-type-infer #'d)
(raise-syntax-error
'match
"Could not infer discrimnant's type. Try using #:in to declare it."
syn))]))
(~optional (~seq #:return ~! maybe-return-type))
(~peek (~seq (~var prec (match-preclause (attribute maybe-return-type))) ...))
~!
(~parse D:inductive-type-declaration (cur-expand (attribute t)))
(~bind (return-type (ormap values (attribute prec.return-type))))
(~do (unless (attribute return-type)
(raise-syntax-error
'match
"Could not infer return type. Try using #:return to declare it."
syn)))
;; BUG TODO: return-type is inferred with the indexes of the branches, but those must be abstracted in the motive...
;; Replace each of the D.indicies with the equivalent name from D.decls
(~bind (motive (quasisyntax/loc syn
(lambda #,@(attribute D.decls)
#,((attribute D.abstract-indices) (attribute return-type))))))
(~var c (match-clause (attribute D) (attribute motive))) ...)
;; TODO: Make all syntax extensions type check, report good error, rather than fail at Curnel
(quasisyntax/loc syn
(elim
D.inductive-name
motive
#,(attribute D.indices)
(c.method ...)
d))]))
(syntax-case syn ()
[(_ e clause* ...)
(let* ([clauses (map clause-parse (syntax->list #'(clause* ...)))]
[R (infer-result clauses)]
[D (or (type-infer/syn #'e)
(raise-syntax-error 'match "Could not infer discrimnant's type." syn))]
[motive #`(lambda (x : #,D) #,R)]
[U (type-infer/syn R)])
#`((elim #,D #,U)
#,motive
#,@(map (curry clause->method D motive) clauses)
e))]))
(begin-for-syntax
(define-syntax-class let-clause
@ -437,14 +190,14 @@
#:attr type (cond
[(attribute t)
;; TODO: Code duplication in ::
(unless (cur-type-check? #'e #'t)
(unless (type-check/syn? #'e #'t)
(raise-syntax-error
'let
(format "Term ~a does not have expected type ~a. Inferred type was ~a"
(cur->datum #'e) (cur->datum #'t) (cur->datum (cur-type-infer #'e)))
(cur->datum #'e) (cur->datum #'t) (cur->datum (type-infer/syn #'e)))
#'e (quasisyntax/loc #'x (x e))))
#'t]
[(cur-type-infer #'e)]
[(type-infer/syn #'e)]
[else
(raise-syntax-error
'let
@ -453,31 +206,31 @@
(define-syntax (let syn)
(syntax-parse syn
[(let (c:let-clause ...) body)
#'((lambda (c.id : c.type) ... body) c.e ...)]))
#'((lambda* (c.id : c.type) ... body) c.e ...)]))
;; Normally type checking will only happen if a term is actually used/appears at top-level.
;; This forces a term to be checked against a particular type.
;; Normally type checking will only happen if a term is actually used. This forces a term to be
;; checked against a particular type.
(define-syntax (:: syn)
(syntax-case syn ()
[(_ pf t)
(begin
;; TODO: Code duplication in let-clause pattern
(unless (cur-type-check? #'pf #'t)
(unless (type-check/syn? #'pf #'t)
(raise-syntax-error
'::
(format "Term ~a does not have expected type ~a. Inferred type was ~a"
(cur->datum #'pf) (cur->datum #'t) (cur->datum (cur-type-infer #'pf)))
(cur->datum #'pf) (cur->datum #'t) (cur->datum (type-infer/syn #'pf)))
syn))
#'(void))]))
(define-syntax (run syn)
(syntax-case syn ()
[(_ expr) (cur-normalize #'expr)]))
[(_ expr) (normalize/syn #'expr)]))
(define-syntax (step syn)
(syntax-case syn ()
[(_ expr)
(let ([t (cur-step #'expr)])
(let ([t (step/syn #'expr)])
(displayln (cur->datum t))
t)]))
@ -493,6 +246,6 @@
(syntax-case syn ()
[(_ term)
(begin
(printf "\"~a\" has type \"~a\"~n" (syntax->datum #'term) (syntax->datum (cur-type-infer #'term)))
(printf "\"~a\" has type \"~a\"~n" (syntax->datum #'term) (syntax->datum (type-infer/syn #'term)))
;; Void is undocumented and a hack, but sort of works
#'(void))]))

View File

@ -1,4 +1,4 @@
#lang s-exp "../../main.rkt"
#lang s-exp "../../cur.rkt"
(require
(for-syntax racket/syntax))
(provide
@ -220,7 +220,7 @@
[pf (proof-state-proof ps)])
(unless (proof-state-proof-complete? ps)
(raise-syntax-error 'qed "Proof contains holes" (pf (current-hole-pretty-symbol))))
(unless (cur-type-check? pf t)
(unless (type-check/syn? pf t)
(raise-syntax-error 'qed "Invalid proof" pf t))
pf)))

View File

@ -1,4 +1,4 @@
#lang s-exp "../../main.rkt"
#lang s-exp "../../cur.rkt"
(require
"base.rkt"
(prefix-in basic: "standard.rkt")

View File

@ -1,4 +1,4 @@
#lang s-exp "../../main.rkt"
#lang s-exp "../../cur.rkt"
(require
"base.rkt"
(for-syntax racket/syntax))
@ -22,7 +22,7 @@
[(forall (x:id : P:expr) body:expr)
(let* ([ps (proof-state-extend-env ps name #'P)]
[ps (proof-state-current-goal-set ps #'body)]
[ps (proof-state-fill-proof-hole ps (lambda (x) #`(λ (#,name : P) #,x)))])
[ps (proof-state-fill-proof-hole ps (lambda (x) #`(lambda (#,name : P) #,x)))])
ps)]
[_ (error 'intro "Can only intro when current goal is of the form (∀ (x : P) body)")]))

View File

@ -1,4 +1,4 @@
#lang s-exp "../main.rkt"
#lang s-exp "../cur.rkt"
(require
"nat.rkt"
"bool.rkt"
@ -38,7 +38,7 @@
#`(define-syntax (#,name syn)
(syntax-case syn ()
[(_ arg args (... ...))
#`(#,(format-id syn "~a-~a" '#,name (cur-type-infer #'arg))
#`(#,(format-id syn "~a-~a" '#,name (type-infer/syn #'arg))
arg
args (... ...))]))))]))
@ -49,7 +49,7 @@
(define (process-def def)
(syntax-case def (define)
[(define (name (a : t) ...) body ...)
(values (syntax->datum #'name) #'(lambda (a : t) ... body ...))]
(values (syntax->datum #'name) #'(lambda* (a : t) ... body ...))]
[(define name body)
(values (syntax->datum #'name) #'body)]))
(syntax-case syn ()
@ -57,7 +57,7 @@
#`(begin
#,@(for/list ([def (syntax->list #'(defs ...))])
(let-values ([(name body) (process-def def)])
(unless (cur-type-check?
(unless (type-check/syn?
body
#`(#,(dict-ref
(dict-ref typeclasses (syntax->datum #'class))

View File

@ -3,5 +3,5 @@
(define deps '("base" ("redex-lib" #:version "1.11")))
(define build-deps '())
(define pkg-desc "implementation (no documentation, tests) part of \"cur\".")
(define version "0.4")
(define version "0.2")
(define pkg-authors '(wilbowma))

View File

@ -0,0 +1,665 @@
#lang racket/base
;; TODO: Copy-paste of redex-core
(require
redex/reduction-semantics
cur/curnel/hybrid-core
rackunit
racket/function
(only-in racket/set set=?))
(define-syntax-rule (check-holds (e ...))
(check-true
(judgment-holds (e ...))))
(define-syntax-rule (check-not-holds (e ...))
(check-false
(judgment-holds (e ...))))
(define-syntax-rule (check-equiv? e1 e2)
(check (default-equiv) e1 e2))
(define-syntax-rule (check-not-equiv? e1 e2)
(check (compose not (default-equiv)) e1 e2))
(default-equiv (lambda (x y) (term (α-equivalent? ,x ,y))))
;; Syntax tests
;; ------------------------------------------------------------------------
(define-term Type (Unv 0))
(check-true (x? (term T)))
(check-true (x? (term A)))
(check-true (x? (term truth)))
(check-true (x? (term zero)))
(check-true (x? (term s)))
(check-true (t? (term zero)))
(check-true (t? (term s)))
(check-true (x? (term nat)))
(check-true (t? (term nat)))
(check-true (t? (term A)))
(check-true (t? (term S)))
(check-true (U? (term (Unv 0))))
(check-true (U? (term Type)))
(check-true (e? (term (λ (x_0 : (Unv 0)) x_0))))
(check-true (t? (term (λ (x_0 : (Unv 0)) x_0))))
(check-true (t? (term (λ (x_0 : (Unv 0)) x_0))))
;; TODO: Rename these signatures, and use them in all future tests.
(define Δ (term (Δ-set* ,(make-empty-Δ)
(nat (Unv 0) ((zero : nat) (s : (Π (x : nat) nat))))
(bool (Unv 0) ((true : bool) (false : bool))))))
(define Δ0 (make-empty-Δ))
(define Δ3 (term (Δ-set ,(make-empty-Δ) and (Π (A : (Unv 0)) (Π (B : (Unv 0)) (Unv 0))) ())))
(define Δ4 (term (Δ-set ,(make-empty-Δ) and (Π (A : (Unv 0)) (Π (B : (Unv 0)) (Unv 0)))
((conj : (Π (A : (Unv 0)) (Π (B : (Unv 0)) (Π (a : A) (Π (b : B) ((and A) B))))))))))
(check-true (Δ? Δ0))
(check-true (Δ? Δ))
(check-true (Δ? Δ4))
(check-true (Δ? Δ3))
(check-true (Δ? Δ4))
(define sigma (term (Δ-set* ,(make-empty-Δ)
(true (Unv 0) ((T : true)))
(false (Unv 0) ())
(equal (Π (A : (Unv 0)) (Π (B : (Unv 0)) (Unv 0)))
())
(nat (Unv 0) ())
(heap (Unv 0) ())
(pre (Π (temp808 : heap) (Unv 0)) ()))))
(displayln sigma)
(check-true (Δ? (term (Δ-set* ,(make-empty-Δ) (true (Unv 0) ((T : true)))))))
(check-true (Δ? (term (Δ-set* ,(make-empty-Δ) (false (Unv 0) ())))))
(check-true (Δ? (term (Δ-set* ,(make-empty-Δ) (equal (Π (A : (Unv 0)) (Π (B : (Unv 0)) (Unv 0)))
())))))
(check-true (Δ? (term (Δ-set* ,(make-empty-Δ) (nat (Unv 0) ())))))
(check-true (Δ? (term (Δ-set* ,(make-empty-Δ) (pre (Π (temp808 : heap) (Unv 0)) ())))))
(check-true (Δ? (term (Δ-set* ,(make-empty-Δ) (true (Unv 0) ((T : true))) (false (Unv 0) ())))))
(check-true (Δ? (term (Δ-set* ,(make-empty-Δ)
(true (Unv 0) ((T : true)))
(false (Unv 0) ())
(equal (Π (A : (Unv 0)) (Π (B : (Unv 0)) (Unv 0)))
())))))
(check-true (Δ? sigma))
(check-true (t? (term (Π (a : A) (Π (b : B) ((and A) B))))))
;; α-equiv and subst tests
;; ------------------------------------------------------------------------
(check-true
(term
(α-equivalent?
(Π (a : S) (Π (b : B) ((and S) B)))
(subst (Π (a : A) (Π (b : B) ((and A) B))) A S))))
;; Various accessor tests
;; ------------------------------------------------------------------------
(check-equal?
(term (Δ-key-by-constructor ,Δ zero))
(term nat))
(check-equal?
(term (Δ-key-by-constructor ,Δ s))
(term nat))
(check-equal?
(term (Δ-ref-constructor-map ,Δ nat))
(term ((zero : nat) (s : (Π (x : nat) nat)))))
(check-equal?
(term (Δ-ref-constructor-map ,sigma false))
(term ()))
;; Telescope tests
;; ------------------------------------------------------------------------
;; Are these telescopes the same when filled with alpha-equivalent, and equivalently renamed, termed
(define (telescope-equiv x y)
(alpha-equivalent? ttL (term (in-hole ,x (Unv 0))) (term (in-hole ,y (Unv 0)))))
(define-syntax-rule (check-telescope-equiv? e1 e2)
(check telescope-equiv e1 e2))
(define-syntax-rule (check-telescope-not-equiv? e1 e2)
(check (compose not telescope-equiv) e1 e2))
(check-telescope-equiv?
(term (Δ-ref-parameter-Ξ ,Δ nat))
(term hole))
(check-telescope-equiv?
(term (Δ-ref-parameter-Ξ ,Δ4 and))
(term (Π (A : Type) (Π (B : Type) hole))))
(check-telescope-equiv?
(term (Ξ-compose
(Π (x : t_0) (Π (y : t_1) hole))
(Π (z : t_2) (Π (a : t_3) hole))))
(term (Π (x : t_0) (Π (y : t_1) (Π (z : t_2) (Π (a : t_3) hole))))))
(check-telescope-equiv?
(term (Δ-methods-telescope ,Δ nat (λ (x : nat) nat)))
(term (Π (m-zero : ((λ (x : nat) nat) zero))
(Π (m-s : (Π (x : nat) (Π (x-ih : ((λ (x : nat) nat) x)) ((λ (x : nat) nat) (s x))))) hole))))
(check-telescope-equiv?
(term (Δ-methods-telescope ,Δ nat P))
(term (Π (m-zero : (P zero))
(Π (m-s : (Π (x : nat) (Π (ih-x : (P x)) (P (s x)))))
hole))))
(check-telescope-equiv?
(term (Δ-methods-telescope ,Δ nat (λ (x : nat) nat)))
(term (Π (m-zero : ((λ (x : nat) nat) zero))
(Π (m-s : (Π (x : nat) (Π (ih-x : ((λ (x : nat) nat) x)) ((λ (x : nat) nat) (s x)))))
hole))))
(check-telescope-equiv?
(term (Δ-methods-telescope ,Δ4 and (λ (A : Type) (λ (B : Type) (λ (x : ((and A) B)) true)))))
(term (Π (m-conj : (Π (A : Type) (Π (B : Type) (Π (a : A) (Π (b : B)
((((λ (A : Type) (λ (B : Type) (λ (x : ((and A) B)) true)))
A)
B)
((((conj A) B) a) b)))))))
hole)))
(check-true (x? (term false)))
(check-true (Ξ? (term hole)))
(check-true (t? (term (λ (y : false) (Π (x : Type) x)))))
(check-true (redex-match? ttL ((x : t) ...) (term ())))
(check-telescope-equiv?
(term (Δ-methods-telescope ,sigma false (λ (y : false) (Π (x : Type) x))))
(term hole))
;; Tests for inductive elimination
;; ------------------------------------------------------------------------
;; TODO: Insufficient tests, no tests of inductives with parameters, or complex induction.
(check-true
(redex-match? tt-ctxtL (in-hole Θ_i (hole (in-hole Θ_r zero))) (term (hole zero))))
(check-telescope-equiv?
(term (Δ-inductive-elim ,Δ nat Type (λ (x : nat) nat) hole
((hole (s zero)) (λ (x : nat) (λ (ih-x : nat) (s (s x)))))
(hole zero)))
(term (hole (((((elim nat Type) (λ (x : nat) nat))
(s zero))
(λ (x : nat) (λ (ih-x : nat) (s (s x)))))
zero))))
(check-telescope-equiv?
(term (Δ-inductive-elim ,Δ nat Type (λ (x : nat) nat) hole
((hole (s zero)) (λ (x : nat) (λ (ih-x : nat) (s (s x)))))
(hole (s zero))))
(term (hole (((((elim nat Type) (λ (x : nat) nat))
(s zero))
(λ (x : nat) (λ (ih-x : nat) (s (s x)))))
(s zero)))))
;; Tests for dynamic semantics
;; ------------------------------------------------------------------------
(check-true (v? (term (λ (x_0 : (Unv 0)) x_0))))
(check-true (v? (term (refl Nat))))
(check-true (v? (term ((refl Nat) z))))
;; TODO: Move equivalence up here, and use in these tests.
(check-equiv? (term (reduce (Unv 0))) (term (Unv 0)))
(check-equiv? (term (reduce ((λ (x : t) x) (Unv 0)))) (term (Unv 0)))
(check-not-equiv? (term (reduce ((Π (x : t) x) (Unv 0)))) (term (Unv 0)))
(check-not-equiv? (term (reduce (Π (x : t) ((Π (x_0 : t) x_0) (Unv 0)))))
(term (Π (x : t) (Unv 0))))
(check-not-equiv? (term (reduce (Π (x : t) ((Π (x_0 : t) (x_0 x)) x))))
(term (Π (x : t) (x x))))
(check-equiv? (term (reduce ,Δ (((((elim nat Type) (λ (x : nat) nat))
(s zero))
(λ (x : nat) (λ (ih-x : nat)
(s (s x)))))
zero)))
(term (s zero)))
(check-equiv? (term (reduce ,Δ (((((elim nat Type) (λ (x : nat) nat))
(s zero))
(λ (x : nat) (λ (ih-x : nat)
(s (s x)))))
(s zero))))
(term (s (s zero))))
(check-equiv? (term (reduce ,Δ (((((elim nat Type) (λ (x : nat) nat))
(s zero))
(λ (x : nat) (λ (ih-x : nat) (s (s x)))))
(s (s (s zero))))))
(term (s (s (s (s zero))))))
(check-equiv?
(term (reduce ,Δ
(((((elim nat Type) (λ (x : nat) nat))
(s (s zero)))
(λ (x : nat) (λ (ih-x : nat) (s ih-x))))
(s (s zero)))))
(term (s (s (s (s zero))))))
(check-equiv?
(term (step ,Δ
(((((elim nat Type) (λ (x : nat) nat))
(s (s zero)))
(λ (x : nat) (λ (ih-x : nat) (s ih-x))))
(s (s zero)))))
(term
(((λ (x : nat) (λ (ih-x : nat) (s ih-x)))
(s zero))
(((((elim nat Type) (λ (x : nat) nat))
(s (s zero)))
(λ (x : nat) (λ (ih-x : nat) (s ih-x))))
(s zero)))))
(check-equiv?
(term (step ,Δ (step ,Δ
(((λ (x : nat) (λ (ih-x : nat) (s ih-x)))
(s zero))
(((((elim nat Type) (λ (x : nat) nat))
(s (s zero)))
(λ (x : nat) (λ (ih-x : nat) (s ih-x))))
(s zero))))))
(term
((λ (ih-x1 : nat) (s ih-x1))
(((λ (x : nat) (λ (ih-x : nat) (s ih-x)))
zero)
(((((elim nat Type) (λ (x : nat) nat))
(s (s zero)))
(λ (x : nat) (λ (ih-x : nat) (s ih-x))))
zero)))))
(define-syntax-rule (check-equivalent e1 e2)
(check-holds (equivalent e1 e2)))
(check-equivalent
(λ (x : Type) x) (λ (y : Type) y))
(check-equivalent
(Π (x : Type) x) (Π (y : Type) y))
;; Test static semantics
;; ------------------------------------------------------------------------
(check-true (term (positive* nat (nat))))
(check-true (term (positive* nat ((Π (x : (Unv 0)) (Π (y : (Unv 0)) nat))))))
(check-true (term (positive* nat ((Π (x : nat) nat)))))
;; (nat -> nat) -> nat
;; Not sure if this is actually supposed to pass
(check-false (term (positive* nat ((Π (x : (Π (y : nat) nat)) nat)))))
;; ((Unv 0) -> nat) -> nat
(check-true (term (positive* nat ((Π (x : (Π (y : (Unv 0)) nat)) nat)))))
;; (((nat -> (Unv 0)) -> nat) -> nat)
(check-true (term (positive* nat ((Π (x : (Π (y : (Π (x : nat) (Unv 0))) nat)) nat)))))
;; Not sure if this is actually supposed to pass
;; ((nat -> nat) -> nat) -> nat
(check-false (term (positive* nat ((Π (x : (Π (y : (Π (x : nat) nat)) nat)) nat)))))
(check-true (judgment-holds (wf ,Δ0 )))
(check-true (redex-match? tt-redL (in-hole Ξ (Unv 0)) (term (Unv 0))))
(check-true (redex-match? tt-redL (in-hole Ξ (in-hole Φ (in-hole Θ nat)))
(term (Π (x : nat) nat))))
(define (bindings-equal? l1 l2)
(map set=? l1 l2))
(check-pred
(curry bindings-equal?
(list (list
(make-bind 'Ξ (term (Π (x : nat) hole)))
(make-bind 'Φ (term hole))
(make-bind 'Θ (term hole)))
(list
(make-bind 'Ξ (term hole))
(make-bind 'Φ (term (Π (x : nat) hole)))
(make-bind 'Θ (term hole)))))
(map match-bindings (redex-match tt-redL (in-hole Ξ (in-hole Φ (in-hole Θ nat)))
(term (Π (x : nat) nat)))))
(check-pred
(curry bindings-equal?
(list
(list
(make-bind 'Φ (term (Π (x : nat) hole)))
(make-bind 'Θ (term hole)))))
(map match-bindings (redex-match tt-redL (in-hole hole (in-hole Φ (in-hole Θ nat)))
(term (Π (x : nat) nat)))))
(check-true
(redex-match? tt-redL
(in-hole hole (in-hole hole (in-hole hole nat)))
(term nat)))
(check-true
(redex-match? tt-redL
(in-hole hole (in-hole (Π (x : nat) hole) (in-hole hole nat)))
(term (Π (x : nat) nat))))
(check-holds (wf (Δ-set ,(make-empty-Δ) nat (Unv 0) ()) ))
(check-holds (wf ,Δ0 ))
(check-holds (type-infer ,(make-empty-Δ) (Unv 0) U))
(check-holds (type-infer ,(make-empty-Δ) ( nat : (Unv 0)) nat U))
(check-holds (type-infer ,(make-empty-Δ) ( nat : (Unv 0)) (Π (x : nat) nat) U))
(check-true (term (positive* nat (nat (Π (x : nat) nat)))))
(check-holds
(wf (Δ-set ,(make-empty-Δ) nat (Unv 0) ((zero : nat))) ))
(check-holds
(wf (Δ-set ,(make-empty-Δ) nat (Unv 0) ((s : (Π (x : nat) nat)))) ))
(check-holds (wf ,Δ ))
;; TODO: Poor abstraction w.r.t. Δ
(check-holds (wf ,Δ3 ))
(check-holds (wf ,Δ4 ))
(check-holds (wf (Δ-set ,(make-empty-Δ) truth (Unv 0) ()) ))
(check-holds (wf ,(make-empty-Δ) ( x : (Unv 0))))
(check-holds (wf (Δ-set ,(make-empty-Δ) nat (Unv 0) ()) ( x : nat)))
(check-holds (wf (Δ-set ,(make-empty-Δ) nat (Unv 0) ()) ( x : (Π (x : nat) nat))))
(check-holds (type-infer ,(make-empty-Δ) (Unv 0) (Unv 1)))
(check-holds (type-infer ,(make-empty-Δ) ( x : (Unv 0)) (Unv 0) (Unv 1)))
(check-holds (type-infer ,(make-empty-Δ) ( x : (Unv 0)) x (Unv 0)))
(check-holds (type-infer ,(make-empty-Δ) (( x_0 : (Unv 0)) x_1 : (Unv 0))
(Π (x_3 : x_0) x_1) (Unv 0)))
(check-holds (type-infer ,(make-empty-Δ) ( x_0 : (Unv 0)) x_0 U_1))
(check-holds (type-infer ,(make-empty-Δ) (( x_0 : (Unv 0)) x_2 : x_0) (Unv 0) U_2))
(check-holds (unv-pred (Unv 0) (Unv 0) (Unv 0)))
(check-holds (type-infer ,(make-empty-Δ) ( x_0 : (Unv 0)) (Π (x_2 : x_0) (Unv 0)) t))
(check-holds (type-check ,(make-empty-Δ) (λ (x : (Unv 0)) x) (Π (x : (Unv 0)) (Unv 0))))
(check-holds (type-check ,(make-empty-Δ) (λ (y : (Unv 0)) (λ (x : y) x))
(Π (y : (Unv 0)) (Π (x : y) y))))
(check-equal? (list (term (Unv 1)))
(judgment-holds
(type-infer ,(make-empty-Δ) (( x1 : (Unv 0)) x2 : (Unv 0)) (Π (t6 : x1) (Π (t2 : x2) (Unv 0)))
U)
U))
;; ---- Elim
;; TODO: Clean up/Reorganize these tests
(check-true
(redex-match? tt-typingL
(in-hole Θ_m (((elim x_D U) e_D) e_P))
(term ((((elim truth Type) T) (Π (x : truth) (Unv 1))) (Unv 0)))))
(define Δtruth (term (Δ-set ,(make-empty-Δ) truth (Unv 0) ((T : truth)))))
(check-holds (type-infer ,Δtruth truth (in-hole Ξ U)))
(check-holds (type-infer ,Δtruth T (in-hole Θ_ai truth)))
(check-holds (type-infer ,Δtruth (λ (x : truth) (Unv 1))
(in-hole Ξ (Π (x : (in-hole Θ truth)) U))))
(check-telescope-equiv?
(term (Δ-methods-telescope ,Δtruth truth (λ (x : truth) (Unv 1))))
(term (Π (m-T : ((λ (x : truth) (Unv 1)) T)) hole)))
(check-holds (type-infer ,Δtruth (elim truth Type) t))
(check-holds (type-check (Δ-set ,(make-empty-Δ) truth (Unv 0) ((T : truth)))
((((elim truth (Unv 2)) (λ (x : truth) (Unv 1))) (Unv 0))
T)
(Unv 1)))
(check-not-holds (type-check (Δ-set ,(make-empty-Δ) truth (Unv 0) ((T : truth)))
((((elim truth (Unv 1)) Type) Type) T)
(Unv 1)))
(check-holds
(type-infer ,(make-empty-Δ) (Π (x2 : (Unv 0)) (Unv 0)) U))
(check-holds
(type-infer ,(make-empty-Δ) ( x1 : (Unv 0)) (λ (x2 : (Unv 0)) (Π (t6 : x1) (Π (t2 : x2) (Unv 0))))
t))
(check-holds
(type-infer ,Δ nat (in-hole Ξ U)))
(check-holds
(type-infer ,Δ zero (in-hole Θ_ai nat)))
(check-holds
(type-infer ,Δ (λ (x : nat) nat)
(in-hole Ξ (Π (x : (in-hole Θ nat)) U))))
(define-syntax-rule (nat-test syn ...)
(check-holds (type-check ,Δ syn ...)))
(nat-test (Π (x : nat) nat) (Unv 0))
(nat-test (λ (x : nat) x) (Π (x : nat) nat))
(nat-test (((((elim nat Type) (λ (x : nat) nat)) zero)
(λ (x : nat) (λ (ih-x : nat) x))) zero)
nat)
(nat-test nat (Unv 0))
(nat-test zero nat)
(nat-test s (Π (x : nat) nat))
(nat-test (s zero) nat)
;; TODO: Meta-function auto-currying and such
(check-holds
(type-infer ,Δ ((((elim nat (Unv 0)) (λ (x : nat) nat))
zero)
(λ (x : nat) (λ (ih-x : nat) x)))
t))
(nat-test (((((elim nat (Unv 0)) (λ (x : nat) nat))
zero)
(λ (x : nat) (λ (ih-x : nat) x)))
zero)
nat)
(nat-test (((((elim nat (Unv 0)) (λ (x : nat) nat))
(s zero))
(λ (x : nat) (λ (ih-x : nat) (s (s x)))))
zero)
nat)
(nat-test (((((elim nat Type) (λ (x : nat) nat))
(s zero))
(λ (x : nat) (λ (ih-x : nat) (s (s x))))) zero)
nat)
(nat-test ( n : nat)
(((((elim nat (Unv 0)) (λ (x : nat) nat)) zero) (λ (x : nat) (λ (ih-x : nat) x))) n)
nat)
(check-holds
(type-check (Δ-set ,Δ bool (Unv 0) ((btrue : bool) (bfalse : bool)))
( n2 : nat)
(((((elim nat (Unv 0)) (λ (x : nat) bool))
btrue)
(λ (x : nat) (λ (ih-x : bool) bfalse)))
n2)
bool))
(check-not-holds
(type-check ,Δ
((((elim nat (Unv 0)) nat) (s zero)) zero)
nat))
(define lam (term (λ (nat : (Unv 0)) nat)))
(check-equivalent
(Π (nat : (Unv 0)) (Unv 0))
,(car (judgment-holds (type-infer ,Δ0 ,lam t) t)))
(check-equivalent
(Π (nat : (Unv 0)) (Unv 0))
,(car (judgment-holds (type-infer ,Δ ,lam t) t)))
(check-equivalent
(Π (x : (Π (y : (Unv 0)) y)) nat)
,(car (judgment-holds (type-infer (Δ-set ,(make-empty-Δ) nat (Unv 0) ()) (λ (x : (Π (y : (Unv 0)) y)) (x nat))
t) t)))
(check-equivalent
(Π (y : (Unv 0)) (Unv 0))
,(car (judgment-holds (type-infer (Δ-set ,(make-empty-Δ) nat (Unv 0) ()) (λ (y : (Unv 0)) y) t) t)))
(check-equivalent
(Unv 0)
,(car (judgment-holds (type-infer (Δ-set ,(make-empty-Δ) nat (Unv 0) ())
((λ (x : (Π (y : (Unv 0)) (Unv 0))) (x nat))
(λ (y : (Unv 0)) y))
t) t)))
(check-equal?
(list (term (Unv 0)) (term (Unv 1)))
(judgment-holds
(type-infer ,Δ4 (Π (S : (Unv 0)) (Π (B : (Unv 0)) (Π (a : S) (Π (b : B) ((and S) B)))))
U) U))
(check-holds
(type-check ,Δ4 ( S : (Unv 0)) conj (Π (A : (Unv 0)) (Π (B : (Unv 0)) (Π (a : A) (Π (b : B) ((and A) B)))))))
(check-holds
(type-check ,Δ4 ( S : (Unv 0))
conj (Π (P : (Unv 0)) (Π (Q : (Unv 0)) (Π (x : P) (Π (y : Q) ((and P) Q)))))))
(check-holds
(type-check ,Δ4 ( S : (Unv 0)) S (Unv 0)))
(check-holds
(type-check ,Δ4 ( S : (Unv 0)) (conj S)
(Π (B : (Unv 0)) (Π (a : S) (Π (b : B) ((and S) B))))))
(check-holds
(type-check ,Δ4 ( S : (Unv 0)) (conj S)
(Π (B : (Unv 0)) (Π (a : S) (Π (b : B) ((and S) B))))))
(check-holds
(type-check ,Δ4 (λ (S : (Unv 0)) (conj S))
(Π (S : (Unv 0)) (Π (B : (Unv 0)) (Π (a : S) (Π (b : B) ((and S) B)))))))
(check-holds
(type-check (Δ-set ,Δ4 true (Unv 0) ((tt : true)))
((((conj true) true) tt) tt)
((and true) true)))
(check-holds
(type-infer ,Δ4 and (in-hole Ξ U_D)))
(check-holds
(type-infer (Δ-set ,Δ4 true (Unv 0) ((tt : true)))
((((conj true) true) tt) tt)
(in-hole Θ and)))
(check-holds
(type-infer (Δ-set ,Δ4 true (Unv 0) ((tt : true)))
(λ (A : Type) (λ (B : Type) (λ (x : ((and A) B)) true)))
(in-hole Ξ (Π (x : (in-hole Θ_Ξ and)) U_P))))
(check-holds
(type-check (Δ-set ,Δ4 true (Unv 0) ((tt : true)))
((((((elim and (Unv 0))
(λ (A : Type) (λ (B : Type) (λ (x : ((and A) B))
true))))
(λ (A : (Unv 0))
(λ (B : (Unv 0))
(λ (a : A)
(λ (b : B) tt)))))
true) true)
((((conj true) true) tt) tt))
true))
(check-true (Γ? (term ((( P : (Unv 0)) Q : (Unv 0)) ab : ((and P) Q)))))
(check-holds
(type-infer ,Δ4 and (in-hole Ξ U)))
(check-holds
(type-infer ,Δ4 ((( P : Type) Q : Type) ab : ((and P) Q))
ab (in-hole Θ and)))
(check-true
(redex-match? tt-redL
(in-hole Ξ (Π (x : (in-hole Θ and)) U))
(term (Π (A : (Unv 0)) (Π (B : (Unv 0)) (Π (x : ((and A) B)) (Unv 0)))))))
(check-holds
(type-infer ,Δ4 ((( P : Type) Q : Type) ab : ((and P) Q))
(λ (A : Type) (λ (B : Type) (λ (x : ((and A) B))
((and B) A))))
(in-hole Ξ (Π (x : (in-hole Θ and)) U))))
(check-holds
(equivalent ,Δ4
(Π (A : (Unv 0)) (Π (B : (Unv 0)) (Π (x : ((and A) B)) (Unv 0))))
(Π (P : (Unv 0)) (Π (Q : (Unv 0)) (Π (x : ((and P) Q)) (Unv 0))))))
(check-holds
(type-infer ,Δ4
(λ (A : Type) (λ (B : Type) (λ (x : ((and A) B))
((and B) A))))
(in-hole Ξ (Π (x : (in-hole Θ_Ξ and)) U_P))))
(check-holds
(type-check ,Δ4
((( P : (Unv 0)) Q : (Unv 0)) ab : ((and P) Q))
((((((elim and (Unv 0))
(λ (A : Type) (λ (B : Type) (λ (x : ((and A) B))
((and B) A)))))
(λ (A : (Unv 0))
(λ (B : (Unv 0))
(λ (a : A)
(λ (b : B) ((((conj B) A) b) a))))))
P) Q) ab)
((and Q) P)))
(check-holds
(type-check (Δ-set ,Δ4 true (Unv 0) ((tt : true)))
(λ (A : Type) (λ (B : Type) (λ (x : ((and A) B)) ((and B) A))))
(Π (A : (Unv 0)) (Π (B : (Unv 0)) (Π (x : ((and A) B)) (Unv 0))))))
(check-holds
(type-infer (Δ-set ,Δ4 true (Unv 0) ((tt : true)))
(( A : Type) B : Type)
(conj B)
t))
(check-holds
(type-check (Δ-set ,Δ4 true (Unv 0) ((tt : true)))
((((((elim and (Unv 0))
(λ (A : Type) (λ (B : Type) (λ (x : ((and A) B))
((and B) A)))))
(λ (A : (Unv 0))
(λ (B : (Unv 0))
(λ (a : A)
(λ (b : B) ((((conj B) A) b) a))))))
true) true)
((((conj true) true) tt) tt))
((and true) true)))
(define gamma (term ( temp863 : pre)))
(check-holds (wf ,sigma ))
(check-holds (wf ,sigma ,gamma))
(check-holds
(type-infer ,sigma ,gamma (Unv 0) t))
(check-holds
(type-infer ,sigma ,gamma pre t))
(check-holds
(type-check ,sigma (,gamma tmp863 : pre) (Unv 0) (Unv 1)))
(check-holds
(type-infer ,sigma ,gamma pre t))
(check-holds
(type-check ,sigma (,gamma tmp863 : pre) (Unv 0) (Unv 1)))
(check-holds
(type-infer ,sigma (,gamma x : false) false (in-hole Ξ U_D)))
(check-holds
(type-infer ,sigma (,gamma x : false) x (in-hole Θ false)))
(check-holds
(type-infer ,sigma (,gamma x : false) (λ (y : false) (Π (x : Type) x))
(in-hole Ξ (Π (x : (in-hole Θ false)) U))))
(check-true
(redex-match? tt-typingL
((in-hole Θ_m ((elim x_D U) e_P)) e_D)
(term (((elim false (Unv 1)) (λ (y : false) (Π (x : Type) x)))
x))))
(check-holds
(type-check ,sigma (,gamma x : false)
(((elim false (Unv 0)) (λ (y : false) (Π (x : Type) x))) x)
(Π (x : (Unv 0)) x)))
;; nat-equal? tests
(define zero?
(term ((((elim nat Type) (λ (x : nat) bool))
true)
(λ (x : nat) (λ (x_ih : bool) false)))))
(check-holds
(type-check ,Δ ,zero? (Π (x : nat) bool)))
(check-equal?
(term (reduce ,Δ (,zero? zero)))
(term true))
(check-equal?
(term (reduce ,Δ (,zero? (s zero))))
(term false))
(define ih-equal?
(term ((((elim nat Type) (λ (x : nat) bool))
false)
(λ (x : nat) (λ (y : bool) (x_ih x))))))
(check-holds
(type-check ,Δ ( x_ih : (Π (x : nat) bool))
,ih-equal?
(Π (x : nat) bool)))
(check-holds
(type-infer ,Δ nat (Unv 0)))
(check-holds
(type-infer ,Δ bool (Unv 0)))
(check-holds
(type-infer ,Δ (λ (x : nat) (Π (x : nat) bool)) (Π (x : nat) (Unv 0))))
(define nat-equal?
(term ((((elim nat Type) (λ (x : nat) (Π (x : nat) bool)))
,zero?)
(λ (x : nat) (λ (x_ih : (Π (x : nat) bool))
,ih-equal?)))))
(check-holds
(type-check ,Δ ( nat-equal? : (Π (x-D«4158» : nat) ((λ (x«4159» : nat) (Π (x«4160» : nat) bool)) x-D«4158»)))
((nat-equal? zero) zero)
bool))
(check-holds
(type-check ,Δ
,nat-equal?
(Π (x : nat) (Π (y : nat) bool))))
(check-equal?
(term (reduce ,Δ ((,nat-equal? zero) (s zero))))
(term false))
(check-equal?
(term (reduce ,Δ ((,nat-equal? (s zero)) zero)))
(term false))
;; == tests
(define Δ= (term (Δ-set ,Δ == (Π (A : (Unv 0)) (Π (a : A) (Π (b : A) (Unv 0))))
((refl : (Π (A : (Unv 0)) (Π (a : A) (((== A) a) a))))))))
(check-true (Δ? Δ=))
(define refl-elim
(term (((((((elim == (Unv 0)) (λ (A1 : (Unv 0)) (λ (x1 : A1) (λ (y1 : A1) (λ (p2 : (((==
A1)
x1)
y1))
nat)))))
(λ (A1 : (Unv 0)) (λ (x1 : A1) zero))) bool) true) true) ((refl bool) true))))
(check-holds
(type-check ,Δ= ,refl-elim nat))
(check-true
(redex-match?
tt-redL
(Δ (in-hole E (in-hole Θ ((elim x_D U) e_P))))
(term (,Δ= ,refl-elim))))
(check-true
(redex-match?
tt-redL
(in-hole (Θ_p (in-hole Θ_i x_ci)) Θ_m)
(term (((((hole
(λ (A1 : (Unv 0)) (λ (x1 : A1) zero))) bool) true) true) ((refl bool) true)))))
(check-telescope-equiv?
(term (Δ-ref-parameter-Ξ ,Δ= ==))
(term (Π (A : Type) (Π (a : A) (Π (b : A) hole)))))
(check-equal?
(term (reduce ,Δ= ,refl-elim))
(term zero))

View File

@ -9,50 +9,77 @@
cur/olly)
(begin-for-syntax
(require rackunit))
;; Can't test this way anymore.
#;(begin-for-syntax
(check-equal?
(format "$$\\begin{array}{lrrl}~n~a~n\\end{array}$$"
(format "\\mbox{\\textit{term}} & e & \\bnfdef & (e1 e2) \\bnfalt (lambda (x) e)\\\\~n"))
(typeset-bnf #'((term (e) ::= (e1 e2) (lambda (x) e)))))
(check-equal?
(format "$$\\begin{array}{lrrl}~n~a~n\\end{array}$$"
(format "\\mbox{\\textit{type}} & A,B,C & \\bnfdef & unit \\bnfalt (* A B) \\bnfalt (+ A C)\\\\~n"))
(typeset-bnf #'((type (A B C) ::= unit (* A B) (+ A C))))))
(require rackunit)
(define (check-id-equal? v1 v2)
(check-equal?
(syntax->datum v1)
(syntax->datum v2)))
(define (check-id-match? v1 v2)
(check-regexp-match
v1
(symbol->string (syntax->datum v2))))
(check-id-match?
#px"term\\d+"
(fresh-name #'term))
(check-id-equal?
#'stlc-lambda
(new-name #'stlc #'lambda))
(check-id-match?
#px"stlc-term\\d+"
(new-name #'stlc (fresh-name #'term))))
(begin-for-syntax
(check-equal?
(format "Inductive nat : Type :=~n| z : nat.~n")
(cur->coq #'(data nat : Type (z : nat))))
(format "$$\\begin{array}{lrrl}~n~a~n\\end{array}$$"
(format "\\mbox{\\textit{term}} & e & \\bnfdef & (e1 e2) \\bnfalt (lambda (x) e)\\\\~n"))
(output-latex-bnf #'(x)
#'((term (e) ::= (e1 e2) (lambda (x) e)))))
(check-equal?
(format "$$\\begin{array}{lrrl}~n~a~n\\end{array}$$"
(format "\\mbox{\\textit{type}} & A,B,C & \\bnfdef & unit \\bnfalt (* A B) \\bnfalt (+ A C)\\\\~n"))
(output-latex-bnf #'(x)
#'((type (A B C) ::= unit (* A B) (+ A C))))))
(check-equal?
(var-equal? (avar z) (avar z))
true)
(check-equal?
(var-equal? (avar z) (avar (s z)))
false)
(begin-for-syntax
(check-equal?
(parameterize ([coq-defns ""]) (output-coq #'(data nat : Type (z : nat))) (coq-defns))
(format "Inductive nat : Type :=~n| z : nat.~n"))
(check-regexp-match
"(forall .+ : Type, Type)"
(cur->coq #'(-> Type Type)))
(let ([t (cur->coq
#'(define-relation (meow gamma term type)
[(g : gamma) (e : term) (t : type)
--------------- T-Bla
(meow g e t)]))])
(output-coq #'(-> Type Type)))
(let ([t (parameterize ([coq-defns ""])
(output-coq #'(define-relation (meow gamma term type)
[(g : gamma) (e : term) (t : type)
--------------- T-Bla
(meow g e t)]))
(coq-defns))])
(check-regexp-match
"Inductive meow : \\(forall .+ : gamma, \\(forall .+ : term, \\(forall .+ : type, Type\\)\\)\\) :="
(first (string-split t "\n")))
(check-regexp-match
"\\| T-Bla : \\(forall g : gamma, \\(forall e : term, \\(forall t : type, \\(\\(\\(meow g\\) e\\) t\\)\\)\\)\\)\\."
(second (string-split t "\n"))))
(let ([t (cur->coq
#'(elim nat (lambda (x : nat) nat)
()
(z (lambda (x : nat) (ih-x : nat) ih-x))
e))])
(let ([t (output-coq #'(elim nat Type (lambda (x : nat) nat) z
(lambda* (x : nat) (ih-x : nat) ih-x)
e))])
(check-regexp-match
"\\(nat_rect \\(fun x : nat => nat\\) z \\(fun x : nat => \\(fun ih_x : nat => ih_x\\)\\) e\\)"
"\\(\\(\\(\\(nat_rect \\(fun x : nat => nat\\)\\) z\\) \\(fun x : nat => \\(fun ih_x : nat => ih_x\\)\\)\\) e\\)"
t))
(check-regexp-match
"Definition thm_plus_commutes := \\(forall n : nat, \\(forall m : nat, \\(\\(\\(== nat\\) \\(\\(plus n\\) m\\)\\) \\(\\(plus m\\) n\\)\\)\\)\\).\n"
(cur->coq
#'(define thm:plus-commutes (forall (n : nat) (m : nat)
(== nat (plus n m) (plus m n))))))
(parameterize ([coq-defns ""])
(output-coq #'(define thm:plus-commutes (forall* (n : nat) (m : nat)
(== nat (plus n m) (plus m n)))))
(coq-defns)))
(check-regexp-match
"Function add1 \\(n : nat\\) := \\(s n\\).\n"
(cur->coq #'(define (add1 (n : nat)) (s n)))))
(parameterize ([coq-defns ""])
(output-coq #'(define (add1 (n : nat)) (s n)))
(coq-defns))))

View File

@ -1,24 +0,0 @@
#lang cur
(require
cur/stdlib/sugar
rackunit)
(data Nat : Type
(z : Nat)
(s : (Π (x : Nat) Nat)))
(plus . : . (-> Nat Nat Nat))
(define (plus n m)
(match n
[z m]
[(s (x : Nat))
(s (recur x))]))
(check-equal?
(plus z z)
z)
(check-equal?
(plus (s z) z)
(s z))

View File

@ -81,6 +81,23 @@
(Π (a : S) (Π (b : B) ((and S) B)))
(subst (Π (a : A) (Π (b : B) ((and A) B))) A S))))
;; Various accessor tests
;; ------------------------------------------------------------------------
(check-equal?
(term (Δ-key-by-constructor ,Δ zero))
(term nat))
(check-equal?
(term (Δ-key-by-constructor ,Δ s))
(term nat))
(check-equal?
(term (Δ-ref-constructor-map ,Δ nat))
(term ((zero : nat) (s : (Π (x : nat) nat)))))
(check-equal?
(term (Δ-ref-constructor-map ,sigma false))
(term ()))
;; Telescope tests
;; ------------------------------------------------------------------------
;; Are these telescopes the same when filled with alpha-equivalent, and equivalently renamed, termed
@ -98,10 +115,41 @@
(term (Δ-ref-parameter-Ξ ,Δ4 and))
(term (Π (A : Type) (Π (B : Type) hole))))
(check-telescope-equiv?
(term (Ξ-compose
(Π (x : t_0) (Π (y : t_1) hole))
(Π (z : t_2) (Π (a : t_3) hole))))
(term (Π (x : t_0) (Π (y : t_1) (Π (z : t_2) (Π (a : t_3) hole))))))
(check-telescope-equiv?
(term (Δ-methods-telescope ,Δ nat (λ (x : nat) nat)))
(term (Π (m-zero : ((λ (x : nat) nat) zero))
(Π (m-s : (Π (x : nat) (Π (x-ih : ((λ (x : nat) nat) x)) ((λ (x : nat) nat) (s x))))) hole))))
(check-telescope-equiv?
(term (Δ-methods-telescope ,Δ nat P))
(term (Π (m-zero : (P zero))
(Π (m-s : (Π (x : nat) (Π (ih-x : (P x)) (P (s x)))))
hole))))
(check-telescope-equiv?
(term (Δ-methods-telescope ,Δ nat (λ (x : nat) nat)))
(term (Π (m-zero : ((λ (x : nat) nat) zero))
(Π (m-s : (Π (x : nat) (Π (ih-x : ((λ (x : nat) nat) x)) ((λ (x : nat) nat) (s x)))))
hole))))
(check-telescope-equiv?
(term (Δ-methods-telescope ,Δ4 and (λ (A : Type) (λ (B : Type) (λ (x : ((and A) B)) true)))))
(term (Π (m-conj : (Π (A : Type) (Π (B : Type) (Π (a : A) (Π (b : B)
((((λ (A : Type) (λ (B : Type) (λ (x : ((and A) B)) true)))
A)
B)
((((conj A) B) a) b)))))))
hole)))
(check-true (x? (term false)))
(check-true (Ξ? (term hole)))
(check-true (t? (term (λ (y : false) (Π (x : Type) x)))))
(check-true (redex-match? ttL ((x : t) ...) (term ())))
(check-telescope-equiv?
(term (Δ-methods-telescope ,sigma false (λ (y : false) (Π (x : Type) x))))
(term hole))
;; Tests for inductive elimination
;; ------------------------------------------------------------------------
@ -109,32 +157,21 @@
(check-true
(redex-match? tt-ctxtL (in-hole Θ_i (hole (in-hole Θ_r zero))) (term (hole zero))))
(check-telescope-equiv?
(term (Δ-inductive-elim ,Δ nat
(elim nat (λ (x : nat) nat) ()
((s zero) (λ (x : nat) (λ (ih-x : nat) (s (s x)))))
hole)
(term (Δ-inductive-elim ,Δ nat Type (λ (x : nat) nat) hole
((hole (s zero)) (λ (x : nat) (λ (ih-x : nat) (s (s x)))))
(hole zero)))
(term (hole (elim nat (λ (x : nat) nat)
()
((s zero)
(λ (x : nat) (λ (ih-x : nat) (s (s x)))))
zero))))
(term (hole (((((elim nat Type) (λ (x : nat) nat))
(s zero))
(λ (x : nat) (λ (ih-x : nat) (s (s x)))))
zero))))
(check-telescope-equiv?
(term (Δ-inductive-elim ,Δ nat
(elim nat (λ (x : nat) nat) ()
((s zero) (λ (x : nat) (λ (ih-x : nat) (s (s x)))))
hole)
(term (Δ-inductive-elim ,Δ nat Type (λ (x : nat) nat) hole
((hole (s zero)) (λ (x : nat) (λ (ih-x : nat) (s (s x)))))
(hole (s zero))))
(term (hole (elim nat (λ (x : nat) nat) ()
((s zero) (λ (x : nat) (λ (ih-x : nat) (s (s x)))))
(s zero)))))
(check-telescope-equiv?
(term (Δ-inductive-elim ,Δ nat
(elim nat (λ (x : nat) nat) ()
((s zero) (λ (x : nat) (λ (ih-x : nat) (s (s x)))))
hole)
hole))
(term hole))
(term (hole (((((elim nat Type) (λ (x : nat) nat))
(s zero))
(λ (x : nat) (λ (ih-x : nat) (s (s x)))))
(s zero)))))
;; Tests for dynamic semantics
;; ------------------------------------------------------------------------
@ -142,8 +179,6 @@
(check-true (v? (term (λ (x_0 : (Unv 0)) x_0))))
(check-true (v? (term (refl Nat))))
(check-true (v? (term ((refl Nat) z))))
(check-true (v? (term zero)))
(check-true (v? (term (s zero))))
;; TODO: Move equivalence up here, and use in these tests.
(check-equiv? (term (reduce (Unv 0))) (term (Unv 0)))
@ -153,71 +188,63 @@
(term (Π (x : t) (Unv 0))))
(check-not-equiv? (term (reduce (Π (x : t) ((Π (x_0 : t) (x_0 x)) x))))
(term (Π (x : t) (x x))))
(check-equal? (term (Δ-constructor-index ,Δ nat zero)) 0)
(check-equiv? (term (reduce ,Δ (elim nat (λ (x : nat) nat)
()
((s zero)
(λ (x : nat) (λ (ih-x : nat) (s (s x)))))
zero)))
(check-equiv? (term (reduce ,Δ (((((elim nat Type) (λ (x : nat) nat))
(s zero))
(λ (x : nat) (λ (ih-x : nat)
(s (s x)))))
zero)))
(term (s zero)))
(check-equiv? (term (reduce ,Δ (elim nat (λ (x : nat) nat)
()
((s zero)
(λ (x : nat) (λ (ih-x : nat) (s (s x)))))
(s zero))))
(check-equiv? (term (reduce ,Δ (((((elim nat Type) (λ (x : nat) nat))
(s zero))
(λ (x : nat) (λ (ih-x : nat)
(s (s x)))))
(s zero))))
(term (s (s zero))))
(check-equiv? (term (reduce ,Δ (elim nat (λ (x : nat) nat)
()
((s zero)
(λ (x : nat) (λ (ih-x : nat) (s (s x)))))
(check-equiv? (term (reduce ,Δ (((((elim nat Type) (λ (x : nat) nat))
(s zero))
(λ (x : nat) (λ (ih-x : nat) (s (s x)))))
(s (s (s zero))))))
(term (s (s (s (s zero))))))
(check-equiv?
(term (reduce ,Δ
(elim nat (λ (x : nat) nat)
()
((s (s zero))
(λ (x : nat) (λ (ih-x : nat) (s ih-x))))
(s (s zero)))))
(((((elim nat Type) (λ (x : nat) nat))
(s (s zero)))
(λ (x : nat) (λ (ih-x : nat) (s ih-x))))
(s (s zero)))))
(term (s (s (s (s zero))))))
(check-equiv?
(term (step ,Δ
(elim nat (λ (x : nat) nat)
()
((s (s zero))
(λ (x : nat) (λ (ih-x : nat) (s ih-x))))
(s (s zero)))))
(((((elim nat Type) (λ (x : nat) nat))
(s (s zero)))
(λ (x : nat) (λ (ih-x : nat) (s ih-x))))
(s (s zero)))))
(term
(((λ (x : nat) (λ (ih-x : nat) (s ih-x)))
(s zero))
(elim nat (λ (x : nat) nat)
()
((s (s zero))
(λ (x : nat) (λ (ih-x : nat) (s ih-x))))
(s zero)))))
(((((elim nat Type) (λ (x : nat) nat))
(s (s zero)))
(λ (x : nat) (λ (ih-x : nat) (s ih-x))))
(s zero)))))
(check-equiv?
(term (step ,Δ (step ,Δ
(((λ (x : nat) (λ (ih-x : nat) (s ih-x)))
(s zero))
(elim nat (λ (x : nat) nat)
()
((s (s zero))
(λ (x : nat) (λ (ih-x : nat) (s ih-x))))
(s zero))))))
(((((elim nat Type) (λ (x : nat) nat))
(s (s zero)))
(λ (x : nat) (λ (ih-x : nat) (s ih-x))))
(s zero))))))
(term
((λ (ih-x1 : nat) (s ih-x1))
(((λ (x : nat) (λ (ih-x : nat) (s ih-x)))
zero)
(elim nat (λ (x : nat) nat)
()
((s (s zero))
(λ (x : nat) (λ (ih-x : nat) (s ih-x))))
zero)))))
(((((elim nat Type) (λ (x : nat) nat))
(s (s zero)))
(λ (x : nat) (λ (ih-x : nat) (s ih-x))))
zero)))))
(define-syntax-rule (check-equivalent e1 e2)
(check-holds (convert e1 e2)))
(check-holds (equivalent e1 e2)))
(check-equivalent
(λ (x : Type) x) (λ (y : Type) y))
(check-equivalent
@ -316,42 +343,28 @@
U))
;; ---- Elim
;; TODO: Clean up/Reorganize these tests
(check-true
(redex-match? tt-typingL
(in-hole Θ_m (((elim x_D U) e_D) e_P))
(term ((((elim truth Type) T) (Π (x : truth) (Unv 1))) (Unv 0)))))
(define Δtruth (term ( (truth : (Unv 0) ((T : truth))))))
(check-holds (type-infer ,Δtruth truth (in-hole Ξ U)))
(check-holds (type-infer ,Δtruth T (in-hole Θ_ai truth)))
(check-holds (type-infer ,Δtruth (λ (x : truth) (Unv 1))
(in-hole Ξ (Π (x : (in-hole Θ truth)) U))))
(check-equiv?
(term (Δ-motive-type ,Δtruth truth (Unv 2)))
(term (Π (x : truth) (Unv 2))))
(check-holds (type-check ,Δtruth (Unv 0) ,(car (term (Δ-method-types ,Δtruth truth (λ (x : truth) (Unv 1)))))))
(check-holds (type-check ,Δtruth (λ (x : truth) (Unv 1)) (Π (x : truth) (Unv 2))))
(check-equiv?
(term (apply (λ (x : truth) (Unv 1)) T))
(term ((λ (x : truth) (Unv 1)) T)))
(check-holds
(convert ,Δtruth (apply (λ (x : truth) (Unv 1)) T) (Unv 1)))
(check-holds (type-infer ,Δtruth
(check-telescope-equiv?
(term (Δ-methods-telescope ,Δtruth truth (λ (x : truth) (Unv 1))))
(term (Π (m-T : ((λ (x : truth) (Unv 1)) T)) hole)))
(check-holds (type-infer ,Δtruth (elim truth Type) t))
(check-holds (type-check ( (truth : (Unv 0) ((T : truth))))
(elim truth (λ (x : truth) (Unv 1))
() ((Unv 0)) T)
t))
(check-holds (type-check ,Δtruth
(elim truth (λ (x : truth) (Unv 1))
() ((Unv 0)) T)
((((elim truth (Unv 2)) (λ (x : truth) (Unv 1))) (Unv 0))
T)
(Unv 1)))
(check-not-holds (type-check ( (truth : (Unv 0) ((T : truth))))
(elim truth Type () (Type) T)
((((elim truth (Unv 1)) Type) Type) T)
(Unv 1)))
(check-holds
(type-infer (Π (x2 : (Unv 0)) (Unv 0)) U))
@ -369,54 +382,47 @@
(check-holds (type-check ,Δ syn ...)))
(nat-test (Π (x : nat) nat) (Unv 0))
(nat-test (λ (x : nat) x) (Π (x : nat) nat))
(nat-test (elim nat (λ (x : nat) nat) ()
(zero (λ (x : nat) (λ (ih-x : nat) x)))
zero)
(nat-test (((((elim nat Type) (λ (x : nat) nat)) zero)
(λ (x : nat) (λ (ih-x : nat) x))) zero)
nat)
(nat-test nat (Unv 0))
(nat-test zero nat)
(nat-test s (Π (x : nat) nat))
(nat-test (s zero) nat)
;; TODO: Meta-function auto-currying and such
(check-holds
(type-infer ,Δ (λ (x : nat)
(elim nat (λ (x : nat) nat)
()
(zero
(λ (x : nat) (λ (ih-x : nat) x)))
x))
(type-infer ,Δ ((((elim nat (Unv 0)) (λ (x : nat) nat))
zero)
(λ (x : nat) (λ (ih-x : nat) x)))
t))
(nat-test (elim nat (λ (x : nat) nat)
()
(zero (λ (x : nat) (λ (ih-x : nat) x)))
zero)
(nat-test (((((elim nat (Unv 0)) (λ (x : nat) nat))
zero)
(λ (x : nat) (λ (ih-x : nat) x)))
zero)
nat)
(nat-test (elim nat (λ (x : nat) nat)
()
((s zero) (λ (x : nat) (λ (ih-x : nat) (s (s x)))))
zero)
(nat-test (((((elim nat (Unv 0)) (λ (x : nat) nat))
(s zero))
(λ (x : nat) (λ (ih-x : nat) (s (s x)))))
zero)
nat)
(nat-test (elim nat (λ (x : nat) nat)
()
((s zero) (λ (x : nat) (λ (ih-x : nat) (s (s x)))))
zero)
(nat-test (((((elim nat Type) (λ (x : nat) nat))
(s zero))
(λ (x : nat) (λ (ih-x : nat) (s (s x))))) zero)
nat)
(nat-test ( n : nat)
(elim nat (λ (x : nat) nat)
()
(zero (λ (x : nat) (λ (ih-x : nat) x)))
n)
(((((elim nat (Unv 0)) (λ (x : nat) nat)) zero) (λ (x : nat) (λ (ih-x : nat) x))) n)
nat)
(check-holds
(type-check (,Δ (bool : (Unv 0) ((btrue : bool) (bfalse : bool))))
( n2 : nat)
(elim nat (λ (x : nat) bool)
()
(btrue (λ (x : nat) (λ (ih-x : bool) bfalse)))
n2)
(((((elim nat (Unv 0)) (λ (x : nat) bool))
btrue)
(λ (x : nat) (λ (ih-x : bool) bfalse)))
n2)
bool))
(check-not-holds
(type-check ,Δ
(elim nat nat () ((s zero)) zero)
((((elim nat (Unv 0)) nat) (s zero)) zero)
nat))
(define lam (term (λ (nat : (Unv 0)) nat)))
(check-equivalent
@ -475,15 +481,15 @@
(in-hole Ξ (Π (x : (in-hole Θ_Ξ and)) U_P))))
(check-holds
(type-check (,Δ4 (true : (Unv 0) ((tt : true))))
(elim and
(λ (A : Type) (λ (B : Type) (λ (x : ((and A) B))
true)))
(true true)
((λ (A : (Unv 0))
(λ (B : (Unv 0))
(λ (a : A)
(λ (b : B) tt)))))
((((conj true) true) tt) tt))
((((((elim and (Unv 0))
(λ (A : Type) (λ (B : Type) (λ (x : ((and A) B))
true))))
(λ (A : (Unv 0))
(λ (B : (Unv 0))
(λ (a : A)
(λ (b : B) tt)))))
true) true)
((((conj true) true) tt) tt))
true))
(check-true (Γ? (term ((( P : (Unv 0)) Q : (Unv 0)) ab : ((and P) Q)))))
(check-holds
@ -501,7 +507,7 @@
((and B) A))))
(in-hole Ξ (Π (x : (in-hole Θ and)) U))))
(check-holds
(convert ,Δ4
(equivalent ,Δ4
(Π (A : (Unv 0)) (Π (B : (Unv 0)) (Π (x : ((and A) B)) (Unv 0))))
(Π (P : (Unv 0)) (Π (Q : (Unv 0)) (Π (x : ((and P) Q)) (Unv 0))))))
(check-holds
@ -512,15 +518,14 @@
(check-holds
(type-check ,Δ4
((( P : (Unv 0)) Q : (Unv 0)) ab : ((and P) Q))
(elim and
(λ (A : Type) (λ (B : Type) (λ (x : ((and A) B))
((and B) A))))
(P Q)
((λ (A : (Unv 0))
(λ (B : (Unv 0))
(λ (a : A)
(λ (b : B) ((((conj B) A) b) a))))))
ab)
((((((elim and (Unv 0))
(λ (A : Type) (λ (B : Type) (λ (x : ((and A) B))
((and B) A)))))
(λ (A : (Unv 0))
(λ (B : (Unv 0))
(λ (a : A)
(λ (b : B) ((((conj B) A) b) a))))))
P) Q) ab)
((and Q) P)))
(check-holds
(type-check (,Δ4 (true : (Unv 0) ((tt : true))))
@ -533,14 +538,14 @@
t))
(check-holds
(type-check (,Δ4 (true : (Unv 0) ((tt : true))))
(elim and
((((((elim and (Unv 0))
(λ (A : Type) (λ (B : Type) (λ (x : ((and A) B))
((and B) A))))
(true true)
((λ (A : (Unv 0))
((and B) A)))))
(λ (A : (Unv 0))
(λ (B : (Unv 0))
(λ (a : A)
(λ (b : B) ((((conj B) A) b) a))))))
true) true)
((((conj true) true) tt) tt))
((and true) true)))
(define gamma (term ( temp863 : pre)))
@ -563,18 +568,21 @@
(check-holds
(type-infer ,sigma (,gamma x : false) (λ (y : false) (Π (x : Type) x))
(in-hole Ξ (Π (x : (in-hole Θ false)) U))))
(check-true
(redex-match? tt-typingL
((in-hole Θ_m ((elim x_D U) e_P)) e_D)
(term (((elim false (Unv 1)) (λ (y : false) (Π (x : Type) x)))
x))))
(check-holds
(type-check ,sigma (,gamma x : false)
(elim false (λ (y : false) (Π (x : Type) x)) () () x)
(((elim false (Unv 0)) (λ (y : false) (Π (x : Type) x))) x)
(Π (x : (Unv 0)) x)))
;; nat-equal? tests
(define zero?
(term (λ (n : nat)
(elim nat (λ (x : nat) bool) ()
(true (λ (x : nat) (λ (x_ih : bool) false)))
n))))
(term ((((elim nat Type) (λ (x : nat) bool))
true)
(λ (x : nat) (λ (x_ih : bool) false)))))
(check-holds
(type-check ,Δ ,zero? (Π (x : nat) bool)))
(check-equal?
@ -584,12 +592,9 @@
(term (reduce ,Δ (,zero? (s zero))))
(term false))
(define ih-equal?
(term (λ (ih : nat)
(elim nat (λ (x : nat) bool)
()
(false
(λ (x : nat) (λ (y : bool) (x_ih x))))
ih))))
(term ((((elim nat Type) (λ (x : nat) bool))
false)
(λ (x : nat) (λ (y : bool) (x_ih x))))))
(check-holds
(type-check ,Δ ( x_ih : (Π (x : nat) bool))
,ih-equal?
@ -601,13 +606,10 @@
(check-holds
(type-infer ,Δ (λ (x : nat) (Π (x : nat) bool)) (Π (x : nat) (Unv 0))))
(define nat-equal?
(term (λ (n : nat)
(elim nat (λ (x : nat) (Π (x : nat) bool))
()
(,zero?
(λ (x : nat) (λ (x_ih : (Π (x : nat) bool))
,ih-equal?)))
n))))
(term ((((elim nat Type) (λ (x : nat) (Π (x : nat) bool)))
,zero?)
(λ (x : nat) (λ (x_ih : (Π (x : nat) bool))
,ih-equal?)))))
(check-holds
(type-check ,Δ ( nat-equal? : (Π (x-D«4158» : nat) ((λ (x«4159» : nat) (Π (x«4160» : nat) bool)) x-D«4158»)))
((nat-equal? zero) zero)
@ -629,12 +631,19 @@
(check-true (Δ? Δ=))
(define refl-elim
(term (elim == (λ (A1 : (Unv 0)) (λ (x1 : A1) (λ (y1 : A1) (λ (p2 : (((== A1) x1) y1)) nat))))
(bool true true)
((λ (A1 : (Unv 0)) (λ (x1 : A1) zero)))
((refl bool) true))))
(term (((((((elim == (Unv 0)) (λ (A1 : (Unv 0)) (λ (x1 : A1) (λ (y1 : A1) (λ (p2 : (((==
A1)
x1)
y1))
nat)))))
(λ (A1 : (Unv 0)) (λ (x1 : A1) zero))) bool) true) true) ((refl bool) true))))
(check-holds
(type-check ,Δ= ,refl-elim nat))
(check-true
(redex-match?
tt-redL
(Δ (in-hole E (in-hole Θ ((elim x_D U) e_P))))
(term (,Δ= ,refl-elim))))
(check-true
(redex-match?
tt-redL

View File

@ -1,49 +0,0 @@
#lang cur
(require
rackunit
cur/stdlib/sugar
cur/stdlib/bool
cur/stdlib/nat
cur/stdlib/maybe
cur/stdlib/list)
(check-equal?
nil
nil)
;; NB HACK: Hack to register :: as a test-case.
;; TODO: Abstract this away
(check-equal?
(void)
(:: (cons Bool true (nil Bool)) (List Bool)))
(check-equal?
(void)
(:: (lambda (A : Type) (a : A)
(ih-a : (-> Nat (Maybe A)))
(n : Nat)
(match n
[z (some A a)]
[(s (n-1 : Nat))
(ih-a n-1)]))
(forall (A : Type) (a : A) (ih-a : (-> Nat (Maybe A)))
(n : Nat)
(Maybe A))))
(check-equal?
(void)
(:: (lambda (A : Type) (n : Nat) (none A)) (forall (A : Type) (-> Nat (Maybe A)))))
(check-equal?
(void)
(:: (elim List (lambda (A : Type) (ls : (List A)) Nat)
(Bool)
((lambda (A : Type) z)
(lambda (A : Type) (a : A) (ls : (List A)) (ih : Nat)
z))
(nil Bool))
Nat))
(check-equal?
(void)
(:: list-ref (forall (A : Type) (-> (List A) Nat (Maybe A)))))
(check-equal?
((list-ref Bool (cons Bool true (nil Bool))) z)
(some Bool true))

View File

@ -11,10 +11,11 @@
(some Bool true))
;; Disabled until #22 fixed
#;(check-equal?
(match (some Bool true)
[(none (A : Type))
false]
[(some (A : Type) (x : A))
;; TODO: Don't know how to use dependency yet
(if x true false)])
(case* Maybe Type (some Bool true) (Bool)
(lambda* (A : Type) (x : (Maybe A)) A)
[(none (A : Type)) IH: ()
false]
[(some (A : Type) (x : A)) IH: ()
;; TODO: Don't know how to use dependency yet
(if x true false)])
true)

View File

@ -11,11 +11,11 @@
(:: pf:proj1 thm:proj1)
(:: pf:proj2 thm:proj2)
(check-equal?
(elim == (λ (A : Type) (x : A) (y : A) (p : (== A x y)) Nat)
(Bool
true
true)
((λ (A : Type) (x : A) z))
(elim == Type (λ* (A : Type) (x : A) (y : A) (p : (== A x y)) Nat)
(λ* (A : Type) (x : A) z)
Bool
true
true
(refl Bool true))
z)

View File

@ -5,19 +5,19 @@
;; TODO: Missing tests for match, others
(check-equal?
((λ (x : (Type 1)) (y : ( (x : (Type 1)) (Type 1))) (y x))
((λ* (x : (Type 1)) (y : (* (x : (Type 1)) (Type 1))) (y x))
Type
(λ (x : (Type 1)) x))
Type)
(check-equal?
((λ (x : (Type 1)) (y : ( (Type 1) (Type 1))) (y x))
((λ* (x : (Type 1)) (y : (* (Type 1) (Type 1))) (y x))
Type
(λ (x : (Type 1)) x))
Type)
(check-equal?
((λ (x : (Type 1)) (y : ( (Type 1) (Type 1))) (y x))
((λ* (x : (Type 1)) (y : ( (Type 1) (Type 1))) (y x))
Type
(λ (x : (Type 1)) x))
Type)

View File

@ -8,10 +8,12 @@
cur/stdlib/typeclass)
(typeclass (Eqv (A : Type))
(equal? : (forall (a : A) (b : A) Bool)))
(equal? : (forall* (a : A) (b : A) Bool)))
(impl (Eqv Bool)
(define (equal? (a : Bool) (b : Bool))
(if a b (not b))))
(if a
(if b true false)
(if b false true))))
(impl (Eqv Nat)
(define equal? nat-equal?))
(check-equal?

View File

@ -2,7 +2,6 @@
(require
rackunit
cur/stdlib/nat
cur/stdlib/list
cur/stdlib/sugar
cur/olly
cur/stdlib/maybe
@ -16,55 +15,65 @@
(val (v) ::= true false unit)
;; TODO: Allow datum, like 1, as terminals
(type (A B) ::= boolty unitty (-> A B) (* A A))
(term (e) ::= x v (app e e) (lambda (#:bind x : A) e) (cons e e)
(let (#:bind x #:bind x) = e in e)))
(term (e) ::= x v (app e e) (lambda (x : A) e) (cons e e)
(let (x x) = e in e)))
(define lookup-env (list-ref stlc-type))
;; TODO: Abstract this over stlc-type, and provide from in OLL
(data Gamma : Type
(emp-gamma : Gamma)
(extend-gamma : (->* Gamma Var stlc-type Gamma)))
(define (extend-env (g : (List stlc-type)) (t : stlc-type))
(cons stlc-type t g))
(define (lookup-gamma (g : Gamma) (x : Var))
(case* Gamma Type g () (lambda* (g : Gamma) (Maybe stlc-type))
[emp-gamma (none stlc-type)]
[(extend-gamma (g1 : Gamma) (v1 : Var) (t1 : stlc-type))
IH: ((ih-g1 : (Maybe stlc-type)))
(if (var-equal? v1 x)
(some stlc-type t1)
ih-g1)]))
(define-relation (has-type (List stlc-type) stlc-term stlc-type)
(define-relation (has-type Gamma stlc-term stlc-type)
#:output-coq "stlc.v"
#:output-latex "stlc.tex"
[(g : (List stlc-type))
[(g : Gamma)
------------------------ T-Unit
(has-type g (stlc-val->stlc-term stlc-unit) stlc-unitty)]
(has-type g (stlc-val-->-stlc-term stlc-unit) stlc-unitty)]
[(g : (List stlc-type))
[(g : Gamma)
------------------------ T-True
(has-type g (stlc-val->stlc-term stlc-true) stlc-boolty)]
(has-type g (stlc-val-->-stlc-term stlc-true) stlc-boolty)]
[(g : (List stlc-type))
[(g : Gamma)
------------------------ T-False
(has-type g (stlc-val->stlc-term stlc-false) stlc-boolty)]
(has-type g (stlc-val-->-stlc-term stlc-false) stlc-boolty)]
[(g : (List stlc-type)) (x : Nat) (t : stlc-type)
(== (Maybe stlc-type) (lookup-env g x) (some stlc-type t))
[(g : Gamma) (x : Var) (t : stlc-type)
(== (Maybe stlc-type) (lookup-gamma g x) (some stlc-type t))
------------------------ T-Var
(has-type g (Nat->stlc-term x) t)]
(has-type g (Var-->-stlc-term x) t)]
[(g : (List stlc-type)) (e1 : stlc-term) (e2 : stlc-term)
[(g : Gamma) (e1 : stlc-term) (e2 : stlc-term)
(t1 : stlc-type) (t2 : stlc-type)
(has-type g e1 t1)
(has-type g e2 t2)
---------------------- T-Pair
(has-type g (stlc-cons e1 e2) (stlc-* t1 t2))]
[(g : (List stlc-type)) (e1 : stlc-term) (e2 : stlc-term)
[(g : Gamma) (e1 : stlc-term) (e2 : stlc-term)
(t1 : stlc-type) (t2 : stlc-type)
(t : stlc-type)
(x : Var) (y : Var)
(has-type g e1 (stlc-* t1 t2))
(has-type (extend-env (extend-env g t1) t2) e2 t)
(has-type (extend-gamma (extend-gamma g x t1) y t2) e2 t)
---------------------- T-Let
(has-type g (stlc-let e1 e2) t)]
(has-type g (stlc-let x y e1 e2) t)]
[(g : (List stlc-type)) (e1 : stlc-term) (t1 : stlc-type) (t2 : stlc-type)
(has-type (extend-env g t1) e1 t2)
[(g : Gamma) (e1 : stlc-term) (t1 : stlc-type) (t2 : stlc-type) (x : Var)
(has-type (extend-gamma g x t1) e1 t2)
---------------------- T-Fun
(has-type g (stlc-lambda t1 e1) (stlc--> t1 t2))]
(has-type g (stlc-lambda x t1 e1) (stlc--> t1 t2))]
[(g : (List stlc-type)) (e1 : stlc-term) (e2 : stlc-term)
[(g : Gamma) (e1 : stlc-term) (e2 : stlc-term)
(t1 : stlc-type) (t2 : stlc-type)
(has-type g e1 (stlc--> t1 t2))
(has-type g e2 t1)
@ -76,57 +85,59 @@
;; TODO: When generating a parser, will need something like (#:name app (e e))
;; so I can name a constructor without screwing with syntax.
(begin-for-syntax
(define (dict-shift d)
(for/fold ([d (make-immutable-hash)])
([(k v) (in-dict d)])
(dict-set d k #`(s #,v)))))
(define index #'z))
(define-syntax (begin-stlc syn)
(let stlc ([syn (syntax-case syn () [(_ e) #'e])]
[d (make-immutable-hash)])
(set! index #'z)
(let stlc ([syn (syntax-case syn () [(_ e) #'e])])
(syntax-parse syn
#:datum-literals (lambda : prj * -> quote let in cons bool)
[(lambda (x : t) e)
#`(stlc-lambda #,(stlc #'t d) #,(stlc #'e (dict-set (dict-shift d) (syntax->datum #'x) #`z)))]
(let ([oldindex index])
(set! index #`(s #,index))
;; Replace x with a de bruijn index, by running a CIC term at
;; compile time.
(normalize/syn
#`((lambda* (x : stlc-term)
(stlc-lambda (avar #,oldindex) #,(stlc #'t) #,(stlc #'e)))
(Var-->-stlc-term (avar #,oldindex)))))]
[(quote (e1 e2))
#`(stlc-cons #,(stlc #'e1 d) #,(stlc #'e2 d))]
#`(stlc-cons #,(stlc #'e1) #,(stlc #'e2))]
[(let (x y) = e1 in e2)
#`(stlc-let #,(stlc #'t d) #,(stlc #'e1 d)
#,(stlc #'e2 (dict-set* (dict-shift (dict-shift d))
(syntax->datum #'x) #`z
(syntax->datum #'y) #`(s z))))]
(let* ([y index]
[x #`(s #,y)])
(set! index #`(s (s #,index)))
#`((lambda* (x : stlc-term) (y : stlc-term)
(stlc-let (avar #,x) (avar #,y) #,(stlc #'t) #,(stlc #'e1)
#,(stlc #'e2)))
(Var-->-stlc-term (avar #,x))
(Var-->-stlc-term (avar #,y))))
#`(let x i #,(stlc #'e1))]
[(e1 e2)
#`(stlc-app #,(stlc #'e1 d) #,(stlc #'e2 d))]
[() #'(stlc-val->stlc-term stlc-unit)]
[#t #'(stlc-val->stlc-term stlc-true)]
[#f #'(stlc-val->stlc-term stlc-false)]
#`(stlc-app #,(stlc #'e1) #,(stlc #'e2))]
[() #'(stlc-val-->-stlc-term stlc-unit)]
[#t #'(stlc-val-->-stlc-term stlc-true)]
[#f #'(stlc-val-->-stlc-term stlc-false)]
[(t1 * t2)
#`(stlc-* #,(stlc #'t1 d) #,(stlc #'t2 d))]
#`(stlc-* #,(stlc #'t1) #,(stlc #'t2))]
[(t1 -> t2)
#`(stlc--> #,(stlc #'t1 d) #,(stlc #'t2 d))]
#`(stlc--> #,(stlc #'t1) #,(stlc #'t2))]
[bool #`stlc-boolty]
[e
(cond
[(eq? 1 (syntax->datum #'e))
#'stlc-unitty]
[(dict-ref d (syntax->datum #'e) #f) =>
(lambda (x)
#`(Nat->stlc-term #,x))]
[else #'e])])))
(if (eq? 1 (syntax->datum #'e))
#'stlc-unitty
#'e)])))
(check-equal?
(begin-stlc (lambda (x : 1) x))
(stlc-lambda stlc-unitty (Nat->stlc-term z)))
(stlc-lambda (avar z) stlc-unitty (Var-->-stlc-term (avar z))))
(check-equal?
(begin-stlc ((lambda (x : 1) x) ()))
(stlc-app (stlc-lambda stlc-unitty (Nat->stlc-term z))
(stlc-val->stlc-term stlc-unit)))
(check-equal?
(begin-stlc (lambda (x : 1) (lambda (y : 1) x)))
(stlc-lambda stlc-unitty (stlc-lambda stlc-unitty (Nat->stlc-term (s z)))))
(stlc-app (stlc-lambda (avar z) stlc-unitty (Var-->-stlc-term (avar z)))
(stlc-val-->-stlc-term stlc-unit)))
(check-equal?
(begin-stlc '(() ()))
(stlc-cons (stlc-val->stlc-term stlc-unit)
(stlc-val->stlc-term stlc-unit)))
(stlc-cons (stlc-val-->-stlc-term stlc-unit)
(stlc-val-->-stlc-term stlc-unit)))
(check-equal?
(begin-stlc #t)
(stlc-val->stlc-term stlc-true))
(stlc-val-->-stlc-term stlc-true))

View File

@ -1,16 +0,0 @@
#lang sweet-exp cur
require
cur/stdlib/sugar
cur/stdlib/bool
cur/stdlib/nat
rackunit
check-equal?
if true false true
false
define + plus
check-equal?
{z + s(z)}
s(z)

View File

@ -1,7 +1,7 @@
#lang info
(define collection 'multi)
(define deps '())
(define build-deps '("base" "rackunit-lib" ("cur-lib" #:version "0.4") "sweet-exp"))
(define build-deps '("base" "rackunit-lib" ("cur-lib" #:version "0.2")))
(define update-implies '("cur-lib"))
(define pkg-desc "Tests for \"cur\".")
(define pkg-authors '(wilbowma))