finished proof of assoc+

This commit is contained in:
wadler 2018-01-01 17:40:30 -02:00
parent 7c7ecbe950
commit bf6c5a0602
4 changed files with 203 additions and 66 deletions

View file

@ -47,7 +47,7 @@ https://github.com/agda/agda/blob/master/src/data/emacs-mode/agda-input.el#L194
## Bindings for Agda mode
? create hole
{!...!} create holde
{!...!} create hole
C-c C-l reload
C-c C-c x split on variable x
C-c C-space fill in hole

View file

@ -22,6 +22,7 @@ http://homepages.inf.ed.ac.uk/wadler/
-->
- [Naturals: Natural numbers]({{ "/Naturals" | relative_url }})
- [Properties: Proof by induction]({{ "/Properties" | relative_url }})
- [Maps: Total and Partial Maps]({{ "/Maps" | relative_url }})
- [Stlc: The Simply Typed Lambda-Calculus]({{ "/Stlc" | relative_url }})
- [StlcProp: Properties of STLC]({{ "/StlcProp" | relative_url }})

View file

@ -42,9 +42,9 @@ as a pair of inference rules:
--------
zero :
n :
m :
---------
suc n :
suc m :
And here is the definition in Agda:
\begin{code}
@ -58,8 +58,9 @@ and `zero` and `suc` (short for *successor*) are the
Both definitions above tell us the same two things:
1. The term `zero` is a natural number.
2. If `n` is a natural number, then the term `suc n` is also a natural number.
+ *Base case*: `zero` is a natural number.
+ *Inductive case*: if `m` is a natural number, then `suc m` is also a
natural number.
Further, these two rules give the *only* ways of creating natural numbers.
Hence, the possible natural numbers are
@ -81,14 +82,14 @@ successor of two; and so on.
## Unpacking the inference rules
Let's unpack the inference rules. Each inference rule consists of zero
or more *judgments* written above a horizontal line, called the *hypotheses*,
and a single judgment written below, called the *conclusion*.
The first rule has no hypotheses, and the conclusion asserts that zero
is a natural. The second rule has one hypothesis, which assumes that
`n` is a natural number, and the conclusion asserts that `suc n` is a
natural number.
Let's unpack the inference rules. Each inference rule consists of
zero or more *judgments* written above a horizontal line, called the
*hypotheses*, and a single judgment written below, called the
*conclusion*. The first rule is the base case. It has no hypotheses,
and the conclusion asserts that `zero` is a natural. The second rule
is the inductive case. It has one hypothesis, which assumes that `m`
is a natural, and the conclusion asserts that `suc n` is a also a
natural.
## Unpacking the Agda definition
@ -112,18 +113,20 @@ corresponding `data` declaration. The lines
tell us that `zero` is a natural number and that `suc` takes a natural
number as argument and returns a natural number.
Here `` and `→` are unicode symbols that you won't find on your
keyboard. At the end of each chapter is a list of all the unicode used
in the chapter, including instructions on how to type them in the
Emacs text editor.
You may have notices that `` and `→` are don't appear on your
keyboard. They are symbols in *unicode*. At the end of each chapter
is a list of all the unicode used in the chapter, including
instructions on how to type them in the Emacs text editor using keys
that are on your keyboard.
## The story of creation
Let's look again at the definition of natural numbers:
1. The term `zero` is a natural number.
2. If `n` is a natural number, then the term `suc n` is also a natural number.
+ *Base case*: `zero` is a natural number.
+ *Inductive case*: if `m` is a natural number, then `suc m` is also a
natural number.
Wait a minute! The second line defines natural numbers
in terms of natural numbers. How can that posssibly be allowed?
@ -139,28 +142,27 @@ no natural numbers at all.
-- in the beginning, there are no natural numbers
Now, we apply the rules to all the natural numbers we know about. One
rule tells us that `zero` is a natural number, so we add it to the set
of known natural numbers. The other rule tells us that if `n` is a
natural number (on the day before today) then `suc n` is also a
Now, we apply the two cases to all the natural numbers we know about. The
base case tells us that `zero` is a natural number, so we add it to the set
of known natural numbers. The inductive case tells us that if `m` is a
natural number (on the day before today) then `suc m` is also a
natural number (today). We didn't know about any natural numbers
before today, so we don't add any natural numbers of the form `suc n`.
before today, so the inductive case doesn't apply.
-- on the first day, there is one natural number
zero
Then we repeat the process, so on the next day we know about all the
numbers from the day before, plus any numbers added by the rules. One
rule tells us that `zero` is a natural number, but we already knew
that. But now the other rule tells us that since `zero` was a natural
number yesterday, `suc zero` is a natural number today.
numbers from the day before, plus any numbers added by the two cases. The
base case tells us that `zero` is a natural number, but we already knew
that. But now the inductive case tells us that since `zero` was a natural
number yesterday, then `suc zero` is a natural number today.
-- on the second day, there are two natural numbers
zero
suc zero
And we repeat the process again. Once more, one rule tells us what
we already knew, that `zero` is a natural number. And now the other rule
And we repeat the process again. Now the inductive case
tells us that since `zero` and `suc zero` are both natural numbers, then
`suc zero` and `suc (suc zero)` are natural numbers. We already knew about
the first of these, but the second is new.
@ -179,9 +181,8 @@ You've probably got the hang of it by now.
suc (suc (suc zero))
The process continues. On the *n*th day there will be *n* distinct
natural numbers. Note that in this way, we only talk about finite sets
of numbers. Every natural number will appear on some given finite
day. In particular, the number *n* first appears on day *n+1*. And we
natural numbers. Every natural number will appear on some given day.
In particular, the number *n* first appears on day *n+1*. And we
never actually define the set of numbers in terms of itself. Instead,
we define the set of numbers on day *n+1* in terms of the set of
numbers on day *n*.
@ -203,10 +204,10 @@ A philosopher might note that our reference to the first day, second
day, and so on, implicitly involves an understanding of natural
numbers. In this sense, our definition might indeed be regarded as in
some sense circular. We won't worry about the philosophy, but are ok
with taking some intuitive notions---such as counting---as given.
with taking some intuitive notions--such as counting--as given.
While the natural numbers have been understood for as long as people
could count, this way of viewing the natural numbers is relatively
could count, the inductive definition of the natural numbers is relatively
recent. It can be traced back to Richard Dedekind's paper "*Was sind
und was sollen die Zahlen?*" (What are and what should be the
numbers?), published in 1888, and Giuseppe Peano's book "*Arithmetices
@ -243,14 +244,13 @@ a machine with 64-bit words.
## Operations on naturals are recursive functions
Now that we have the natural numbers, what can we do with them?
An obvious first step is to define basic operations such as
addition and multiplication.
For instance, can we define arithmetic operations such as
addition and multiplication?
As a child I spent much time memorising tables of addition and
multiplication. At first the rules seemed tricky and I would often
make mistakes. So it came as a shock to realise that all of addition
can be precisely defined in just a couple of lines, and the same is true of
multiplication.
make mistakes. So it came as a shock to me to realise that all of addition
and multiplication can be precisely defined in just a couple of lines.
Here is the definition of addition in Agda:
\begin{code}

View file

@ -4,22 +4,23 @@ layout : page
permalink : /Properties
---
Now that we've defined the naturals and operations upon them,
our next step is to learn how to prove properties that they
satisfy. Unsurprisingly, properties of *inductive datatypes*
are proved by *induction*.
Now that we've defined the naturals and operations upon them, our next
step is to learn how to prove properties that they satisfy. As hinted
by their name, properties of *inductive datatypes* are proved by
*induction*.
## Imports
Each chapter will begin with a list of the imports we require from the
Agda standard library. We will, of course, require the naturals;
Agda standard library.
<!-- We will, of course, require the naturals;
everything in the previous chapter is also found in the library module
`Data.Nat`, so we import the required definitions from there. We also
require propositional equality.
`Data.Nat`, so we import the required definitions from there.
We also require propositional equality. -->
\begin{code}
open import Data.Nat using (; zero; suc)
open import Relation.Binary.PropositionalEquality using (_≡_; refl)
\end{code}
@ -54,18 +55,40 @@ variables.
3 + (4 + 5)
Here we have displayed the computation in tabular form, one term to a
line. We will often use such a form. It is often easiest to read
such computations from the top down until one reaches the simplest
possible term (in this case, 12), and then from the bottom up until
one reaches the same term.
line. It is often easiest to read such computations from the top down
until one reaches the simplest term (in this case, `12`), and
then from the bottom up until one reaches the same term.
We could go on testing the proposition by choosing other numbers, but---since there are an infinite number of naturals---we will never finish.
The test reveals that associativity is perhaps not as obvious as first
it appears. Why should `7 + 5` be the same as `3 + 9`? We might want
to gather more evidence, testing the proposition by choosing other
numbers. But---since there are an infinite number of
naturals---testing can never be complete. Is there any way we can be
sure that associativity holds for *all* the natural numbers?
The answer is yes! We can prove a property holds for all naturals using
*proof by induction*.
## Proof by induction
Recall the definition of natural numbers.
\begin{code}
data : Set where
zero :
suc :
\end{code}
This tells us that `zero` is a natural--the *base case*--and that if
`m` is a natural then `suc m` is also a natural---the *inductive
case*.
Proofs by induction follow the structure of this definition. To prove
a property of natural numbers by induction, we need prove two cases.
First is the *base case*, where we need to show the property holds for
`zero`. Second is the *inductive case*, where we assume as the
*inductive hypothesis* that the property holds for an arbitary natural
`m` and then show that the property must also hold for `suc m`.
To prove a property of natural numbers by induction, we need to prove two things.
First, we prove the property holds for `zero`, and secondly we prove that if
the property holds for an arbitrary natural `m` then it also holds for `suc m`.
If we write `P m` for a property of `m`, then we can write out the principle
of induction as an inference rule:
@ -74,13 +97,61 @@ of induction as an inference rule:
-----------------------------
∀ (m : ) → P m
Let's unpack this rule. The first hypothesis states
that property `P` holds for `zero`. The second hypothesis states that for all natural
numbers `m`, if `P` holds for `m` then `P` also holds for `suc m`. The conclusion
states that for all natural numbers `m` we have that `P` holds for `m`.
We call the first hypothesis the *base case*; it requires we show `P zero`.
We call the second hypothesis the *inductive case*; it requires we assume `P m`, which
we call the *induction hypothesis*, and use it to show `P (suc m)`.
Let's unpack this rule. The first hypothesis is the base case, and
requires we show that property `P` holds for `zero`. The second
hypothesis is the inductive case, and requires that for any natural
`m` assuming the the induction hypothesis `P` holds for `m` we must
then show that `P` also holds for `suc m`.
Why does this work? Again, it can be explained by a creation story.
To start with, we know no properties.
-- in the beginning, no properties are known
Now, we apply the two cases to all the properties we know about. The
base case tells us that `P zero` holds, so we add it to the set of
known properties. The inductive case tells us that if `P m` holds (on
the day before today) then `P (suc m)` also holds (today). We didn't
know about any properties before today, so the inductive case doesn't
apply.
-- on the first day, one property is known
P zero
Then we repeat the process, so on the next day we know about all the
properties from the day before, plus any properties added by the two
cases. The base case tells us that `P zero` holds, but we already
knew that. But now the inductive case tells us that since `P zero`
held yesterday, then `P (suc zero)` holds today.
-- on the second day, two properties are known
P zero
P (suc zero)
And we repeat the process again. Now the inductive case
tells us that since `P zero` and `P (suc zero)` both hold, then
`P (suc zero)` and `P (suc (suc zero))` also hold. We already knew about
the first of these, but the second is new.
-- on the third day, three properties are known
P zero
P (suc zero)
P (suc (suc zero))
You've probably got the hang of it by now.
-- on the fourth day, four properties are known
P zero
P (suc zero)
P (suc (suc zero))
P (suc (suc (suc zero)))
The process continues. On the *n*th day there will be *n* distinct
properties that hold. The property of every natural number will appear
on some given day. In particular, the property `P n` first appears on
day *n+1*.
## Our first proof: associativity
In order to prove associativity, we take `P m` to be the property
@ -108,7 +179,7 @@ For the base case, we must show:
(zero + n) + p ≡ zero + (n + p)
By (i), both sides of the equation simplify to `n + p`, so it holds.
In tabular form, we write this as follows:
In tabular form we have:
(zero + n) + p
≡ (i)
@ -116,8 +187,9 @@ In tabular form, we write this as follows:
≡ (i)
zero + (n + p)
It is often easiest to read such proofs down from the top and up from
the bottom, meeting in the middle where the two terms are the same.
Again, it is easiest to read from the top down until one reaches the
simplest term (`n + p`), and then from the bottom up
until one reaches the same term.
For the inductive case, we assume
@ -141,9 +213,73 @@ these follow from what we assumed. In tabular form:
≡ (ii)
suc m + (n + p)
Here it is easiest to read from the top down until one reaches the
simplest term (`suc ((m + n) + p)`), and then from the bottom up until
one reaches the simplest term (`suc (m + (n + p))`). In this case,
the two simplest terms are not the same, but are equated by the
induction hypothesis.
## Encoding the proof in Agda
We can encode this proof in Agda as follows.
\begin{code}
assoc+ : ∀ (m n p : ) → (m + n) + p ≡ m + (n + p)
assoc+ zero n p = refl
assoc+ (suc m) n p rewrite assoc+ m n p = refl
\end{code}
Here we have named the proof `assoc+`. In Agda, identifiers can consist of
any sequence of characters not including spaces or the characters `@.(){};_`.
Let's unpack this code. The first line states that we are
defining the identifier `assoc+` which is a proof of the
proposition
∀ (m n p : ) → (m + n) + p ≡ m + (n + p)
Such a proof is a function that takes three natural
numbers---corresponding to `m`, `n`, and `p`---and returns
a proof of the proposition `(m + n) + p ≡ m + (n + p)`.
Proof by induction corresponds exactly to a recursive
definition. Here we are inducting on the first
argument `m`, and leaving the other two arguments `n` and `p`
fixed.
The base case corresponds to instantiating `m` by
pattern matching on `zero`, so what we are required to
prove is:
(zero + n) + p ≡ zero + (n + p)
After simplifying with the definition of addition, this equation
becomes:
n + p ≡ n + p
The proof that a term is equal to itself is written `refl`.
The inductive case corresponds to instantiating `m` by `suc zero`,
so what we are required to prove is:
(suc m + n) + p ≡ suc m + (n + p)
After simplifying with the definition of addition, this equation
becomes:
suc ((m + n) + p) ≡ suc (m + (n + p))
After rewriting with the inductive hypothesis these two terms are
equal, and the proof is again given by `refl`.
Rewriting by a given equation is indicated by the keyword `rewrite`
followed by a proof of that equation. Here the inductive hypothesis
is not assumed, but instead proved by a recursive invocation of the
function we are definining, `assoc+ m n p`. As with addition, this is
well founded because associativity of larger numbers is defined in
terms of associativity of smaller numbers. In this case,
`assoc (suc m) n p` is defined in terms of `assoc m n p`.
This correspondence between proof by induction and definition by
recursion is one of the most appealing aspects of Agda.
## Unicode