7.7
The Pie Reference
Link to this document with
@other-doc['(lib "pie/pie.scrbl")]
Link to this document with
@other-doc['(lib "pie/pie.scrbl")]
David Thrane Christiansen and Daniel P. Friedman
Pie is a little language with dependent types that accompanies
The Little Typer.
1 Using Pie in DrRacket
Link to this section with
@secref["Using_Pie_in_DrRacket" #:doc '(lib "pie/pie.scrbl")]
Link to this section with
@secref["Using_Pie_in_DrRacket" #:doc '(lib "pie/pie.scrbl")]
Pie is implemented as a Racket language. While other editors may work, Pie
is currently supported best in DrRacket.
After installing Pie from the Racket package database, open DrRacket.
Change the first line to read #lang pie, and then start typing.
If DrRacket’s "Background Expansion" feature is enabled, the type checker
will run every time the file is modified, whether or not it is run.
Otherwise, invoke the type checker using the "Run" button. It may be
convenient to enable the option in DrRacket to show gold highlighting on
errors, because it causes type errors to be highlighted while typing.
Alternatively, it is also possible to use any editor to create a Pie program,
and then save it to a file. Run the file with the command-line version of
Racket to test it.
The
Todo List for DrRacket
is supported by Pie. Each TODO in the program is listed.
2 General-Purpose Expressions
Link to this section with
@secref["General-Purpose_Expressions" #:doc '(lib "pie/pie.scrbl")]
Link to this section with
@secref["General-Purpose_Expressions" #:doc '(lib "pie/pie.scrbl")]
Asserts that expr is a type. This can be necessary when there is
insufficient information for Pie to discover an expression’s type.
Examples:
> nil |
Can't determine a type |
|
|
> (cons 2 (same 2)) |
Can't determine a type |
|
(the (Σ ((n Nat)) | (= Nat n n)) | (cons 2 | (same 2))) |
|
|
(the (Σ ((n Nat)) | (= Nat n 2)) | (cons 2 | (same 2))) |
|
TODO represents a part of a program that is not yet written, corresponding
to the empty boxes in
The Little Typer. Users may optionally leave a note
or other expression behind as a reminder.
3 Types, Constructors, and Eliminators
Link to this section with
@secref["Types__Constructors__and_Eliminators" #:doc '(lib "pie/pie.scrbl")]
Link to this section with
@secref["Types__Constructors__and_Eliminators" #:doc '(lib "pie/pie.scrbl")]
3.1 Absurd
Link to this section with
@secref["Absurd" #:doc '(lib "pie/pie.scrbl")]
Link to this section with
@secref["Absurd" #:doc '(lib "pie/pie.scrbl")]
Absurd is a type with no values.
Example:
|
(the (→ Absurd | Nat) | (λ (nope) | (ind-Absurd (the Absurd nope) | Nat))) |
|
3.2 Trivial
Link to this section with
@secref["Trivial" #:doc '(lib "pie/pie.scrbl")]
Link to this section with
@secref["Trivial" #:doc '(lib "pie/pie.scrbl")]
Trivial is a type with exactly one value.
3.3 Atoms
Link to this section with
@secref["Atoms" #:doc '(lib "pie/pie.scrbl")]
Link to this section with
@secref["Atoms" #:doc '(lib "pie/pie.scrbl")]
Atoms are like Lisp symbols.
Each atom is a constructor.
quote is always written with a single
tick mark.
Examples:
> 'grønkål |
(the Atom 'grønkål) |
> 'agurk |
(the Atom 'agurk) |
3.4 Natural Numbers
Link to this section with
@secref["Natural_Numbers" #:doc '(lib "pie/pie.scrbl")]
Link to this section with
@secref["Natural_Numbers" #:doc '(lib "pie/pie.scrbl")]
The natural numbers, called
Nat, are all the numbers greater than or equal to zero.
Examples:
> (which-Nat 0 0 (λ (smaller) smaller)) |
(the Nat 0) |
> (which-Nat 17 0 (λ (smaller) smaller)) |
(the Nat 16) |
(iter-Nat target base step) → X
|
target : Nat |
base : X |
step : (-> X X) |
iter-Nat applies
step to
base target times.
rec-Nat is primitive recursion on
Nat. If
target is
zero, then the whole expression is
base. If
target is
(add1 n), then the whole expression is
(step n (rec-Nat n base step)).
(ind-Nat target motive base step) → (motive target)
|
target : Nat |
motive : (-> Nat U) |
base : (motive zero) |
|
ind-Nat is induction on
Nat.
motive is an
(→ Nat U),
and the whole expression’s type is
(motive target).
ind-Nat
computes identically to
rec-Nat; the type is, however, more expressive.
3.5 Pairs
Link to this section with
@secref["Pairs" #:doc '(lib "pie/pie.scrbl")]
Link to this section with
@secref["Pairs" #:doc '(lib "pie/pie.scrbl")]
(Σ ((x A1) (y A2) ...) D)
|
The values of
(Σ ((x A)) D) are
(cons a d), where
a is an
A and the type of
d is found by consistently
replacing
x with
a in
D.
(Σ ((x A1) (y A2) ...) D) is an abbreviation for the nested
Σ-expressions
(Σ ((x A1)) |
(Σ ((y A2)) |
... |
D)) |
(Sigma ((x A1) (y A2) ...) D)
|
A shorter way of writing
(Σ ((x A)) D) when
x is not used.
(cons a d) → (Σ ((x A)) D)
|
a : A |
d : D^ |
cons is the constructor for
Σ.
D^ is the result of
consistently replacing each
x in
D with
a.
(car p) → A
|
p : (Σ ((x A)) D) |
The first projection of a pair. If
p is a
(Σ ((x A)) D), then
(car p) is an
A. Furthermore,
(car (cons a d)) is
a.
(cdr p) → D
|
p : (Σ ((x A)) D) |
The second projection of a pair. If
p is a
(Σ ((x A)) D), then
(cdr p) is a
D where each
x has been replaced by
(car p).
Furthermore,
(cdr (cons a d)) is
d.
3.6 Functions
Link to this section with
@secref["Functions" #:doc '(lib "pie/pie.scrbl")]
Link to this section with
@secref["Functions" #:doc '(lib "pie/pie.scrbl")]
(Π ((x X1) (y X2) ...) B)
|
Function types are written with
Π.
All functions take exactly one argument, and what appears to be a multiple-argument
function or function type is actually a Curried function. In other words,
(Π ((x X1) (y X2) ...) B) is an abbreviation for
(Π ((x X1)) |
(Π ((y X2)) |
... |
B)) |
The type of a function application is found by substituting the actual argument for
the argument name in the return type.
Examples:
|
(the (= Nat 5 5) | (same 5)) |
|
|
(the (= Nat 15 15) | (same 15)) |
|
(Pi ((x X1) (y X2) ...) B)
|
(∏ ((x X1) (y X2) ...) B)
|
∏ is an alias for
Π that is easier to type on some keyboards.
→, pronounced "arrow", is shorter way of writing
(Π ((x X1) (x X2) ...) B) when the identifiers
x ... are not used.
Functions are constructed with
λ.
(λ (x1 x2 ...) b) is a
(Π ((x1 X1) (x2 X2) ...) B) if when
x1 is a
X1,
x2 is a
X2, ...,
then
b is a
B.
What may appear to be multiple-argument functions are actually nested one-argument functions.
Examples:
|
(the (→ Atom Atom | Atom) | (λ (food beverage) | food)) |
|
|
(the (→ Atom Atom | Atom) | (λ (food beverage) | food)) |
|
|
3.7 Lists
Link to this section with
@secref["Lists" #:doc '(lib "pie/pie.scrbl")]
Link to this section with
@secref["Lists" #:doc '(lib "pie/pie.scrbl")]
(List E) is the type of lists in which all entries are
Es.
Examples:
> (:: 0 (:: 1 (:: 2 nil))) |
(the (List Nat) | (:: 0 | (:: 1 | (:: 2 nil)))) |
|
> (:: 0 (:: 'one (:: 2 nil))) |
Expected |
(List Atom) |
but given |
(List Nat) |
|
rec-List is primitive recursion on lists.
If
target is
nil, the result is
base. If
target
is
(:: e es), then the result is
(step e es (rec-List es base step)).
(ind-List target motive base step) → (motive target)
|
target : (List E) |
motive : (-> (List E) U) |
base : (motive nil) |
| step | | : | | (Π ((e E) | (es (List E))) | (-> (motive es) | (motive (:: e es)))) |
|
|
ind-List is induction on lists. When
target is a
(List E), t
the whole expression’s type is
(motive target), the type of
base is
(motive nil), and the type of
step is
(Π ((e E) |
(es (List E))) |
(→ (motive es) |
(motive (:: e es)))) |
Examples:
|
(the Nat 2) |
|
(the (= (List Atom) | (:: 'ananas | (:: 'granatæble nil)) | (:: 'ananas | (:: 'granatæble nil))) | (same (:: 'ananas | (:: 'granatæble nil)))) |
|
3.8 Vectors
Link to this section with
@secref["Vectors" #:doc '(lib "pie/pie.scrbl")]
Link to this section with
@secref["Vectors" #:doc '(lib "pie/pie.scrbl")]
A
(Vec E len) is a list that contains precisely
len entries,
each of which is an
E.
head finds the first entry in a non-empty
Vec.
tail finds the all but the first entry in a non-empty
Vec.
(ind-Vec target-1 target-2 motive base step)
|
→ (motive target-1 target-2) |
target-1 : Nat |
target-2 : (Vec E target-1) |
|
base : (motive zero vecnil) |
|
Induction on vectors is used to prove things about any vector.
target-1 is the length, and target-2 is the vector itself.
The motive is a
The
base is a
(motive zero vecnil),
and the
step is a
3.9 Either
Link to this section with
@secref["Either" #:doc '(lib "pie/pie.scrbl")]
Link to this section with
@secref["Either" #:doc '(lib "pie/pie.scrbl")]
Either represents that there are two possibilities: either an
L
with
left on top, or an
R with
right on top.
(ind-Either target motive on-left on-right) → (motive target)
|
target : (Either X Y) |
motive : (→ (Either X Y) U) |
| on-left | | : | | (Π ((l L)) | (motive (left l))) |
|
|
| on-right | | : | | (Π ((r R)) | (motive (right r))) |
|
|
Induction on
Either consists of showing how to fulfill the motive for
both constructors.
3.10 Equality
Link to this section with
@secref["Equality" #:doc '(lib "pie/pie.scrbl")]
Link to this section with
@secref["Equality" #:doc '(lib "pie/pie.scrbl")]
The equality type’s values are evidence that from and to
are equal.
(same e) → (= X e e)
|
e : X |
If
e is an
X, then
(same e) is an
(= X e e), because
e is the same
X as
e.
(replace target motive base) → (motive to)
|
target : (= X from to) |
motive : (→ X U) |
base : (motive from) |
If
target is an
(= X from to),
motive is an
and
base is a
(motive from), then
(replace target motive base) is a
(motive to).
(symm target) → (= A to from)
|
target : (= A from to) |
If
target is an
(= A from to), then
(symm target) is an
(= A to from).
Example:
|
(the (Π ((x Nat) | (y Nat)) | (→ (= Nat x y) | (= Nat y x))) | (λ (x y p) | (symm p))) |
|
(trans target-1 target-2) → (= X from to)
|
target-1 : (= X from middle) |
target-2 : (= X middle to) |
trans is used to "glue together" evidence of equality. If
target-1
is an
(= X from middle) and
target-2 is an
(= X middle to),
then
(trans target-1 target-2) is an
(= X from to).
(cong target fun) → (= Y (fun from) (fun to))
|
target : (= X from to) |
fun : (→ X Y) |
cong shows that all functions respect equality. In particular,
if
target is an
(= X from to) and
fun is an
then
(cong target fun) is an
(= Y (fun from) (fun to))
(ind-= target motive base) → (motive to target)
|
target : (= A from to) |
| motive | | : | | (Π ((x A)) | (-> (= A from x) | U)) |
|
|
base : (motive from (same from)) |
The induction principle on equality evidence takes a target which is an
(= A from to), a motive which is a
(Π ((x A)) |
(-> (= A from x) |
U)) |
and a base which is a
(motive from (same from)). The entire expression
is then a
(motive to target).
3.11 Universe
Link to this section with
@secref["Universe" #:doc '(lib "pie/pie.scrbl")]
Link to this section with
@secref["Universe" #:doc '(lib "pie/pie.scrbl")]
The universe describes all types except itself and those types that could contain
U.
4 Declarations
Link to this section with
@secref["Declarations" #:doc '(lib "pie/pie.scrbl")]
Link to this section with
@secref["Declarations" #:doc '(lib "pie/pie.scrbl")]
In addition to expressions, Pie has three syntactic forms that are only valid
at the top level of a program.
4.1 Definitions
Link to this section with
@secref["Definitions" #:doc '(lib "pie/pie.scrbl")]
Link to this section with
@secref["Definitions" #:doc '(lib "pie/pie.scrbl")]
Before using
define to associate a name with an expression, it is first necessary to associate
the expression’s type with the name using
claim.
Associate the expression
expr with the name
x, after having already
claimed its type.
4.2 Testing Pie programs
Link to this section with
@secref["Testing_Pie_programs" #:doc '(lib "pie/pie.scrbl")]
Link to this section with
@secref["Testing_Pie_programs" #:doc '(lib "pie/pie.scrbl")]
Check that expr1 is the same type as expr2, and fail if not.
Examples:
Because of the η-rules for Π and Absurd, every proof of a negation
is the same as every other proof. This is useful when testing programs that
produce proofs.
Example: