myTestlll-1.0.0: HSoM/Induction.lhs
%-*- mode: Latex; abbrev-mode: true; auto-fill-function: do-auto-fill -*-
%include lhs2TeX.fmt
%include myFormat.fmt
\chapter{Proof by Induction}
\label{ch:induction}
In this chapter we will study a powerful proof technique based on
\emph{mathematical induction}. With it we will be able to prove
complex and important properties of programs that cannot be
accomplished with proof-by-calculation alone. The inductive proof
method is one of the most powerful and common methods for proving
program properties.
\section{Induction and Recursion}
\index{induction} \index{recursion}
\emph{Induction} is very closely related to \emph{recursion}. In
fact, in certain contexts the terms are used interchangeably; in
others, one is preferred over the other primarily for historical
reasons. Think of them as being duals of one another: induction is
used to describe the process of starting with something small and
simple, and building up from there, whereas recursion describes the
process of starting with something large and complex, and working
backward to the simplest case.
For example, although we have previously used the phrase
\emph{recursive data type}, in fact data types are often described
\emph{inductively}, such as a list:
\begin{quote}
A \emph{list} is either empty, or it is a pair consisting of a value
and another list.
\end{quote}
On the other hand, we usually describe functions that manipulate
lists, such as |map| and |foldr|, as being recursive. This is
because when you apply a function such as |map|, you apply it
initially to the whole list, and work backwards toward |[]|.
But these differences between induction and recursion run no deeper:
they are really just two sides of the same coin.
This chapter is about \emph{inductive properties} of programs (but
based on the above argument could just as rightly be called
\emph{recursive properties}) that are not usually proven via
calculation alone. Proving inductive properties usually involves the
inductive nature of data types and the recursive nature of functions
defined on the data types.
As an example, suppose that |p| is an inductive property of a list.
In other words, |p(l)| for some list |l| is either true or false (no
middle ground!). To prove this property inductively, we do so based
on the length of the list: starting with length 0, we first prove
|p([])| (using our standard method of proof-by-calculation).
Now for the key step: assume for the moment that |p(xs)| is true for
any list |xs| whose length is less than or equal to |n|. Then if we
can prove (via calculation) that |p(x:xs)| is true for any |x|---i.e.\
that |p| is true for lists of length |n+1|---then the claim is that
|p| is true for lists of \emph{any} (finite) length.
Why is this so? Well, from the first step above we know that |p| is
true for length 0, so the second step tells us that it is also true for
length 1. But if it is true for length 1 then it must also be true for
length 2; similarly for lengths 3, 4, etc.\ So |p| is true for lists
of any length!
(It it important to realize, however, that a property being true for
every finite list does not necessarily imply that it is true for every
infinite list. The property ``the list is finite'' is a perfect
example of this! We will see how to prove properties of infinite lists
in Chapter \ref{ch:streams}.) \index{list!infinite}
To summarize, to prove a property |p| by induction on the length of a
list, we proceed in two steps:
\begin{enumerate}
\item Prove |p([])| (this is called the \emph{base case}).
\item Assume that |p(xs)| is true (this is called the \emph{induction
hypothesis}, and prove that |p(x:xs)| is true (this is called the
\emph{induction step}).
\end{enumerate}
\section{Examples of List Induction}
\label{sec:list-examples}
Ok, enough talk, let's see this idea in action. Recall in Section
\ref{sec:poly-types} the following property about \indexwdhs{foldr}:
\[(\forall|xs|)\ \ |foldr (:) [] xs ===> xs|\]
We will prove this by induction on the length of |xs|. Following
the ideas above, we begin with the base case by proving the property
for length 0; i.e.\ for |xs = []|:
\begin{spec}
foldr (:) [] []
==> { unfold foldr }
[]
\end{spec}
This step is immediate from the definition of |foldr|. Now for
the induction step: we first \emph{assume} that the property is true
for all lists |xs| of length |n|, and then prove the property for
list |x:xs|. Again proceeding by calculation:
\begin{spec}
foldr (:) [] (x:xs)
==> { unfold foldr }
x : foldr (:) [] xs
==> { induction hypothesis }
x : xs
\end{spec}
And we are done; the induction hypothesis is what justifies the second
step.
Now let's do something a bit harder. Suppose we are interested in
proving the following property:
\[(\forall|xs,ys|)\ \ |length (xs ++ ys) = length xs + length ys|\]
Our first problem is to decide which list to perform the induction
over. A little thought (in particular, a look at how the definitions
of \indexwdhs{length} and |(++)| are structured) should convince you that
|xs| is the right choice. (If you do not see this, you are
encouraged to try the proof by induction over the length of |ys|!)
Again following the ideas above, we begin with the base case by
proving the property for length 0; i.e.\ for |xs = []|:
\begin{spec}
length ([] ++ ys)
==> { unfold (++) }
length ys
==> { fold (+) }
0 + length ys
==> { fold length }
length [] + length ys
\end{spec}
For the induction step, we first assume that the property is true for
all lists |xs| of length |n|, and then prove the property for list
|x:xs|. Again proceeding by calculation:
\begin{spec}
length ((x:xs) ++ ys)
==> { unfold (++) }
length (x : (xs ++ ys))
==> { unfold length }
1 + length (xs ++ ys)
==> { induction hypothesis }
1 + (length xs + length ys)
==> { associativity of (+) }
(1 + length xs) + length ys
==> { fold length }
length (x:xs) + length ys
\end{spec}
And we are done. The transition from the 3rd line to the 4th is where
the induction hypothesis is used.
\section{Proving Function Equivalences}
At this point it is a simple matter to return to Chapter~\ref{ch:poly}
and supply the proofs that functions defined using |map| and |fold|
are equivalent to the recursively defined versions. In particular,
recall these two definitions of |toAbsPitches|:
\begin{spec}
toAbsPitches1 [] = []
toAbsPitches1 (p:ps) = absPitch p : toAbsPitches1 ps
toAbsPitches2 = map absPitch
\end{spec}
We want to prove that |toAbsPitches1 = toAbsPitches2|. To do so, we
use the extensionality principle (briefly discussed in
Section~\ref{sec:currying-simplification}), which says that two
functions are equal if, when applied to the same value, they always
yield the same result. We can change the specification slightly to
reflect this. For any finite list |ps|, we want to prove:
\begin{spec}
toAbsPitches1 ps = toAbsPitches2 ps
\end{spec}
We proceed by induction, starting with the base case |ps = []|:
\begin{spec}
toAbsPitches1 []
==> []
==> map absPitch []
==> toAbsPitches2 []
\end{spec}
Next we assume that |toAbsPitches1 ps = toAbsPitches2 ps| holds, and
try to prove that |toAbsPitches1 (p:ps) = toAbsPitches2 (p:ps)|:
\begin{spec}
toAbsPitches1 (p:ps)
==> absPitch p : toAbsPitches1 ps
==> absPitch p : toAbsPitches2 ps
==> absPitch p : map absPitch ps
==> map absPitch (p:ps)
\end{spec}
Note the use of the induction hypothesis in the second step.
%% The proof that the two versions of |toPitches| given in
%% Chapter~\ref{ch:poly} is very similar, and is left as an exercise.
For a proof involving |foldr|, recall from Chapter~\ref{ch:poly} this
recursive definition of |line|:
\begin{spec}
line1 [] = rest 0
line1 (m:ms) = m :+: line1 ms
\end{spec}
and this non-recursive version:
\begin{spec}
line2 = foldr (:+:) (rest 0)
\end{spec}
We can prove that these two functions are equivalent by induction.
First the base case:
\begin{spec}
line1 []
==> rest 0
==> foldr (:+:) (rest 0) []
==> line2 []
\end{spec}
Then the induction step:
\begin{spec}
line1 (m:ms)
==> m :+: line1 ms
==> m :+: line2 ms
==> m :+: foldr (:+:) (rest 0) ms
==> foldr (:+:) (rest 0) (m:ms)
==> line2 (m:ms)
\end{spec}
The proofs of equivalence of the definitions of |toPitches|, |chord|,
|maxPitch|, and |hList| from Chapter~\ref{ch:poly} are similar, and
left as an exercise.
\vspace{.1in}\hrule
\begin{exercise}{\em
From Chapter~\ref{ch:poly}, prove that the original recursive versions
of the following functions are equivalent to the versions using |map|
or |fold|: |toPitches|, |chord|, |maxPitch|, and |hList|.}
\end{exercise}
\vspace{.1in}\hrule
\subsection{[Advanced] Reverse}
The proofs of function equivalence in the last section were fairly
straightforward. For something more challenging, consider the
definition of \indexwdhs{reverse} given in Section \ref{sec:reverse}:
\begin{spec}
reverse1 [] = []
reverse1 (x:xs) = reverse1 xs ++ [x]
\end{spec}
and the version given in Section \ref{sec:currying}:
\begin{spec}
reverse2 xs = foldl (flip (:)) [] xs
\end{spec}
We would like to show that these are the same; i.e.\ that
|reverse1 xs = reverse2 xs| for any finite list |xs|. In
carrying out this proof one new idea will be demonstrated, namely
the need for an \emph{auxiliary property}
which is proved independently of the main result.
%% the first being that induction can be used to prove the equivalence of
%% two programs. The second is
The base case is easy, as it often is:
\begin{spec}
reverse1 []
==> []
==> foldl (flip (:)) [] []
==> reverse2 []
\end{spec}
Assume now that |reverse1 xs = reverse2 xs|. The induction step
proceeds as follows:
\begin{spec}
reverse1 (x:xs)
==> reverse1 xs ++ [x]
==> reverse2 xs ++ [x]
==> foldl (flip (:)) [] xs ++ [x]
==> ???
\end{spec}
But now what do we do? Intuitively, it seems that the following
property, which we will call property (1), should hold:
\begin{spec}
foldl (flip (:)) [] xs ++ [x]
==> foldl (flip (:)) [] (x:xs)
\end{spec}
in which case we could complete the proof as follows:
\begin{spec}
...
==> foldl (flip (:)) [] xs ++ [x]
==> foldl (flip (:)) [] (x:xs)
==> reverse2 (x:xs)
\end{spec}
The ability to see that if we could just prove one thing, then perhaps
we could prove another, is a useful skill in conducting proofs. In
this case we have reduced the overall problem to one of proving
property (1), which simplifies the structure of the proof, although
not necessarily the difficulty. These auxiliary properties are often
called \emph{lemmas} in mathematics, and in many cases their proofs
become the most important contributions, since they are often at the
heart of a problem.
In fact if you try to prove property (1) directly, you will run into a
problem, namely that it is not \emph{general} enough. So first let's
generalize property (1) (while renaming |x| to |y|), as follows:
\index{generalization}
\begin{spec}
foldl (flip (:)) ys xs ++ [y]
==> foldl (flip (:)) (ys++[y]) xs
\end{spec}
Let's call this property (2). If (2) is true for any finite |xs|
and |ys|, then property (1) is also true, because:
\begin{spec}
foldl (flip (:)) [] xs ++ [x]
==> { property (2) }
foldl (flip (:)) ([]++[x]) xs
==> { unfold (++) }
foldl (flip (:)) [x] xs
==> { fold (flip (:)) }
foldl (flip (:)) (flip (:) [] x) xs
==> { fold foldl }
foldl (flip (:)) [] (x:xs)
\end{spec}
You are encouraged to try proving property (1) directly, in which case
you will likely come to the same conclusion, namely that the property
needs to be generalized. This is not always easy to see, but is
sometimes an important step is constructing a proof, because, despite
being somewhat counterintuitive, it is often the case that making a
property more general (and therefore more powerful) makes it easier to
prove.
In any case, how do we prove property (2)? Using induction, of
course! Setting |xs| to |[]|, the base case is easy:
\begin{spec}
foldl (flip (:)) ys [] ++ [y]
==> { unfold foldl }
ys++[y]
==> { fold foldl }
foldl (flip (:)) (ys++[y]) []
\end{spec}
and the induction step proceeds as follows:
\begin{spec}
foldl (flip (:)) ys (x:xs) ++ [y]
==> { unfold foldl }
foldl (flip (:)) (flip (:) ys x) xs ++ [y]
==> { unfold flip }
foldl (flip (:)) (x:ys) xs ++ [y]
==> { induction hypothesis }
foldl (flip (:)) ((x:ys)++[y]) xs
==> { unfold (++) }
foldl (flip (:)) (x:(ys++[y])) xs
==> { fold foldl }
foldl (flip (:)) (ys++[y]) (x:xs)
\end{spec}
\out{
Here is why the generalization is needed. If we just try to prove:
foldl (flip (:)) [] xs ++ [x]
==> foldl (flip (:)) [x] xs
Then the base case is fine:
foldl (flip (:)) [] [] ++ [x]
==> { unfold foldl }
[]++[x]
==> { unfold (++) }
[x]
==> { fold foldl }
foldl (flip (:)) [x] []
But the induction step runs into trouble:
foldl (flip (:)) [] (x:xs) ++ [y]
==> { unfold foldl }
foldl (flip (:)) (flip (:) [] x) xs ++ [y]
==> { unfold flip }
foldl (flip (:)) [x] xs ++ [y]
==> ???
What now? We are stuck. In particular, we cannot apply the induction
hypothesis because foldl's third argument is |[x]| and not |[]|.
}
\section{Useful Properties on Lists}
\label{sec:list-properties}
There are many useful properties of functions on lists that require
inductive proofs. Figures \ref{fig:list-props1} and
\ref{fig:list-props2} list a number of them involving functions used
in this text, but their proofs are left as exercises (except for one;
see below). You may assume that these properties are true, and use
them freely in proving other properties of your programs. In fact,
some of these properties can be used to simplify the proof that
|reverse1| and |reverse2| are the same; see if you can find
them!\footnote{More thorough discussions of these properties and their
proofs may be found in \cite{birdwadler88,bird98}.}
(Note, by the way, that in the first rule for |map| in Figure
\ref{fig:list-props1}, the type of |\x -> x| on the left-hand
side is |a->b|, whereas on the right-hand side it is |[a]->[b]|;
i.e. these are really two different functions.)
\begin{figure}
\cbox{
\begin{minipage}{4.75in}
{\bf Properties of |map|:}
\begin{spec}
map (\x->x) = \x->x
map (f . g) = map f . map g
map f . tail = tail . map f
map f . reverse = reverse . map f
map f . concat = concat . map (map f)
map f (xs ++ ys) = map f xs ++ map f ys
\end{spec}
For all strict |f|:
\begin{spec}
f . head = head . map f
\end{spec}
\vspace{0.1in}
{\bf Properties of the |fold| functions:}
\begin{enumerate}
\item If |op| is associative, and |e `op` x = x| and |x `op` e = x|
for all |x|, then for all finite |xs|:
\begin{spec}
foldr op e xs = foldl op e xs
\end{spec}
\item If the following are true:
\begin{spec}
x `op1` (y `op2` z) = (x `op1` y) `op2` z
x `op1` e = e `op2` x
\end{spec}
then for all finite |xs|:
\begin{spec}
foldr op1 e xs = foldl op2 e xs
\end{spec}
\item For all finite |xs|:
\begin{spec}
foldr op e xs = foldl (flip op) e (reverse xs)
\end{spec}
\end{enumerate}
\end{minipage}}
\caption{Some Useful Properties of |map| and |fold|.}
\label{fig:list-props1}
\end{figure}
\begin{figure}
\cbox{
\begin{minipage}{4.75in}
{\bf Properties of |(++)|:}
\vspace{0.1in} For all |xs|, |ys|, and |zs|:
\begin{spec}
(xs ++ ys) ++ zs = xs ++ (ys ++ zs)
xs ++ [] = [] ++ xs = xs
\end{spec}
\vspace{0.1in}
{\bf Properties of |take| and |drop|:}
\vspace{0.1in}
\begin{spec}
take m . take n = take (min m n)
drop m . drop n = drop (m + n)
take m . drop n = drop n . take (m + n)
\end{spec}
For all non-negative |m| and |n| such that $n \geq m$:
\begin{spec}
drop m . take n = take (n - m) . drop m
\end{spec}
For all non-negative |m| and |n|, and finite |xs|:
\begin{spec}
take n xs ++ drop n xs = xs
\end{spec}
\vspace{0.1in}
{\bf Properties of |reverse|:}
\vspace{0.1in} For all finite |xs|:
\begin{spec}
reverse (reverse xs) = xs
head (reverse xs) = last xs
last (reverse xs) = head xs
\end{spec}
\end{minipage}}
\caption{Useful Properties of Other Functions Over Lists}
\label{fig:list-props2}
\end{figure}
\subsection{[Advanced] Function Strictness}
\index{function!strict} \index{bottom}
Note that the last rule for |map| in Figure \ref{fig:list-props1}
is only valid for \emph{strict} functions. A function |f| is said to
be strict if |f bottom| $=$ |bottom|. Recall from Section
\ref{sec:expressions} that |bottom| is the value associated with a
non-terminating computation. So another way to think about a strict
function is that it is one that, when applied to a non-terminating
computation, results in a non-terminating computation. For example,
the successor function |(+1)| is strict, because |(+1) bottom|
$=$ |bottom + 1| $=$ |bottom|. In other words, if you apply
|(+1)| to a non-terminating computation, you end up with a
non-terminating computation.
Not all functions in Haskell are strict, and we have to be careful to
say on which argument a function is strict. For example, |(+)| is
strict on both of its arguments, which is why the section |(+1)| is
also strict. On the other hand, the constant function:
\begin{spec}
const x y = x
\end{spec}
is strict on its first argument (why?), but not its second, because
|const x bottom| $=$ |x|, for any |x|.
\indexhs{(\&\&)}
\syn{Understanding strictness requires a careful understanding of
Haskell's pattern-matching rules. For example, consider the
definition of |(&&)| from the Standard Prelude:
\begin{spec}
(&&) :: Bool -> Bool -> Bool
True && x = x
False && _ = False
\end{spec}
\index{pattern!matching}
When choosing a pattern to match, Haskell starts with the top,
left-most pattern, and works to the right and downward. So in the
above, |(&&)| first evaluates its left argument. If that value is
|True|, then the first equation succeeds, and the second argument
gets evaluated because that is the value that is returned. But if the
first argument is |False|, the second equation succeeds. In
particular, \emph{it does not bother to evaluate the second argument at
all}, and simply returns |False| as the answer. This means that
|(&&)| is strict in its first argument, but not its second.
A more detailed discussion of pattern matching is found in Appendix
\ref{ch:patterns}.
}
Let's now look more closely at the last law for |map|, which says
that for all strict |f|:
\begin{spec}
f . head = head . map f
\end{spec}
Let's try to prove this property, starting with the base case, but
ignoring for now the strictness constraint on |f|:
\begin{spec}
f (head [])
==> f bottom
\end{spec}
|head []| is an error, which you will recall has value |bottom|.
So you can see immediately that the issue of strictness might play a
role in the proof, because without knowing anything about |f|,
there is no further calculation to be done here. Similarly, if we
start with the right-hand side:
\begin{spec}
head (map f [])
==> head []
==> bottom
\end{spec}
It should be clear that for the base case to be true, it must be that
|f bottom| $=$ |bottom|; i.e., |f| must be strict. Thus we
have essentially ``discovered'' the constraint on the theorem through
the process of trying to prove it! (This is not an uncommon
phenomenon.)
The induction step is less problematic:
\begin{spec}
f (head (x:xs))
==> f x
==> head (f x : map f xs)
==> head (map f (x:xs))
\end{spec}
and we are done.
\vspace{.1in}\hrule
\begin{exercise}{\em
Prove as many of the properties in Figures \ref{fig:list-props1} and
\ref{fig:list-props2} as you can.}
\end{exercise}
\begin{exercise}{\em
Which of the following functions are strict (if the function takes
more than one argument, specify on which arguments it is strict):
|reverse|, |simple|, |map|, |tail|, |dur|, |revM|,
|(&&)|, |(True &&)|, |(False &&)|, and
the following function:
\begin{spec}
ifFun :: Bool -> a -> a -> a
ifFun pred cons alt = if pred then cons else alt
\end{spec}
}
\end{exercise}
\vspace{.1in}\hrule
\vspace{.1in}
\section{Induction on the Music Data Type}
\label{sec:induction-others}
Proof by induction is not limited to lists. In particular, we can use
it to reason about |Music| values.
For example, recall this property intuitively conjectured in
Section~\ref{sec:music-fold}:
\begin{spec}
mFold Prim (:+:) (:=:) Modify m = m
\end{spec}
To prove this, we again use the extensionality principle, and then
proceed by induction. But what is the base case? Recall that the
|Music| data type is defined as:
\begin{spec}
data Music a =
Prim (Primitive a)
| Music a :+: Music a
| Music a :=: Music a
| Modify Control (Music a)
\end{spec}
The only constructor that does not take a |Music| value as an argument
is |Prim|, so that in fact is the only base case.
So, starting with this base case:
\begin{spec}
mFold Prim (:+:) (:=:) Modify (Prim p)
==> Prim p
==> id (Prim p)
\end{spec}
That was easy! Next, we develop an induction step for each of the
three non-base cases:
\begin{spec}
mFold Prim (:+:) (:=:) Modify (m1 :+: m2)
==> mFold Prim (:+:) (:=:) Modify m1 :+:
mFold Prim (:+:) (:=:) Modify m2
==> m1 :+: m2
==> id (m1 :+: m2)
\end{spec}
\begin{spec}
mFold Prim (:+:) (:=:) Modify (m1 :=: m2)
==> mFold Prim (:+:) (:=:) Modify m1 :=:
mFold Prim (:+:) (:=:) Modify m2
==> m1 :=: m2
==> id (m1 :=: m2)
\end{spec}
\begin{spec}
mFold Prim (:+:) (:=:) Modify (Modify c m)
==> Modify c (mFold Prim (:+:) (:=:) Modify m)
==> Modify c m
==> id (Modify c m)
\end{spec}
These three steps were quite easy as well, but is not something we
could have done without induction.
For something more challenging, let's consider the following:
\begin{spec}
dur (revM m) = dur m
\end{spec}
%% , if dur m /= bottom
%% The side condition adds an extra twist to this problem.
Again we proceed by induction, starting with the base case:
\begin{spec}
dur (revM (Prim p))
==> dur (Prim p)
\end{spec}
Sequential composition is straightforward:
\begin{spec}
dur (revM (m1 :+: m2))
==> dur (revM m2 :+: revM m1)
==> dur (revM m2) + dur (revM m1)
==> dur m2 + dur m1
==> dur m1 + dur m2
==> dur (m1 :+: m2)
\end{spec}
But things get more complex with parallel composition:
\begin{spec}
dur (revM (m1 :=: m2))
==> dur ( let d1 = dur m1
d2 = dur m2
in if d1>d2 then revM m1 :=: (rest (d1-d2) :+: revM m2)
else (rest (d2-d1) :+: revM m1) :=: revM m2)
==> let d1 = dur m1
d2 = dur m2
in if d1>d2 then dur (revM m1 :=: (rest (d1-d2) :+: revM m2))
else dur ((rest (d2-d1) :+: revM m1) :=: revM m2)
...
\end{spec}
At this point, to make things easier to understand, we will consider
each branch of the conditional in turn. First the consequent branch:
\begin{spec}
dur (revM m1 :=: (rest (d1-d2) :+: revM m2))
==> max (dur (revM m1)) (dur (rest (d1-d2) :+: revM m2))
==> max (dur m1) (dur (rest (d1-d2) :+: revM m2))
==> max (dur m1) (dur (rest (d1-d2)) + dur (revM m2))
==> max (dur m1) ((d1-d2) + dur m2)
==> max (dur m1) (dur m1)
==> dur m1
\end{spec}
And then the alternative:
\begin{spec}
dur ((rest (d2-d1) :+: revM m1) :=: revM m2)
==> max (dur ((rest (d2-d1) :+: revM m1)) (dur (revM m2))
==> max (dur ((rest (d2-d1) :+: revM m1)) (dur m2)
==> max (dur (rest (d2-d1)) + dur (revM m1)) (dur m2)
==> max ((d2-d1) + dur m1) (dur m2)
==> max (dur m2) (dur m2)
==> dur m2
\end{spec}
Now we can continue the proof from above:
\begin{spec}
...
==> let d1 = dur m1
d2 = dur m2
in if d1>d2 then dur m1
else dur m2
==> max (dur m1) (dur m2)
==> dur (m1 :=: m2)
\end{spec}
The final inductive step involves the |Modify| constructor, but recall
that |dur| treats a |Tempo| modification specially, and thus we treat
it specially as well:
\begin{spec}
dur (revM (Modify (Tempo r) m))
==> dur (Modify (Tempo r) (revM m))
==> dur (revM m) / r
==> dur m / r
==> dur (Modify (Tempo r) m)
\end{spec}
Finally, we consider the case that |c /= Tempo r|:
\begin{spec}
dur (revM (Modify c m))
==> dur (Modify c (revM m))
==> Modify c (dur (revM m))
==> Modify c (dur m)
==> dur (Modify c m)
\end{spec}
And we are done.
\vspace{.1in}\hrule
\vspace{.1in}
\begin{exercise}{\em
Recall Exercises \ref{ex:chrom} and \ref{ex:mkscale}. Prove that, if
|p2 >= p1|:
\begin{spec}
chrom p1 p2 = mkScale p1 (take (absPitch p2 - absPitch p1)
(repeat 1))
\end{spec}
using the lemma:
\begin{spec}
[m..n] = scanl (+) m (take (n-m) (repeat 1))
\end{spec}
}
\end{exercise}
%% \begin{exercise}{\em
%% Prove that:}
%% \begin{code}
%% mFold (:+:) (:=:) Prim Modify = id
%% \end{code}
%% \end{exercise}
\begin{exercise}{\em
Prove the following facts involving |dur|:}
\begin{spec}
dur (timesM n m) = n * dur m
dur (takeM d m) = d, if d <= dur m
\end{spec}
%% dur (revM m) = dur m, if dur m /= bottom
\end{exercise}
\begin{exercise}{\em
Prove the following facts involving |mMap|:}
\begin{spec}
mMap id m = m
mMap f (mMap g m) = mMap (f . g) m
\end{spec}
\end{exercise}
\begin{exercise}{\em
Prove that, for all |pmap|, |c|, and |m|:
\begin{spec}
perf pmap c m = (perform pmap c m, dur m)
\end{spec}
where |perform| is the function defined in Figure \ref{fig:perform}.
}
\end{exercise}
\vspace{.1in}\hrule
\vspace{.1in}
\subsection{The Need for Musical Equivalence}
In Chapter \ref{ch:intro} we discussed the need for a notion of
\emph{musical equivalence}, noting that, for example, |m :+: rest 0|
``sounds the same'' as |m|, even if the two |Music| values are not
equal as Haskell values. That same issue can strike us here as we try
to prove intuitively natural properties such as:
\begin{spec}
revM (revM m) = m
\end{spec}
To see why this property cannot be proved without a notion of musical
equivalence, note that:
\begin{spec}
revM (revM (c 4 en :=: d 4 qn))
===> revM ((rest en :+: c 4 en) :=: d 4 qn)
===> (rest 0 :+: c 4 en :+: rest en) :=: d 4 qn
\end{spec}
Clearly the last line above is not equal, as a Haskell value, to |c 4
en :=: d 4 qn|. But somehow we need to show that these two values
``sound the same'' as musical values. In the next chapter we will
formally develop the notion of musical equivalence, and with it be
able to prove the validity of our intuitions regarding |revM|, as well
as many other important musical properties.
\section{[Advanced] Induction on Other Data Types}
Proof by induction can be used to reason about many data types. For
example, we can use it to reason about natural
numbers.\footnote{Indeed, one could argue that a proof by induction
over finite lists is really an induction over natural numbers, since
it is an induction over the \emph{length} of the list, which is a
natural number.} Suppose we define an exponentiation function as
follows: \index{|(^)|}
\begin{spec}
(^) :: Integer -> Integer -> Integer
x^0 = 1
x^n = x * x^(n-1)
\end{spec}
\syn{|(*)| is defined in the Standard Prelude to have precedence
level 7, and recall that if no |infix| declaration is given for an
operator it defaults to \indexwd{precedence} level 9, which means that
|(^)| has precedence level 9, which is higher than that for
|(*)|. Therefore no parentheses are needed to disambiguate the
last line in the definition above, which corresponds nicely to
mathematical convention.}
Now suppose that we want to prove that:
\[(\forall x, n\geq0, m\geq0)\ \ |x^(n+m) = x^n * x^m|\]
We proceed by induction on |n|, beginning with |n=0|:
\begin{spec}
x^(0+m)
==> x^m
==> 1 * (x^m)
==> x^0 * x^m
\end{spec}
Next we assume that the property is true for numbers less than or
equal to |n|, and prove it for |n+1|:
\begin{spec}
x^((n+1)+m)
==> x * x^(n+m)
==> x * (x^n * x^m)
==> (x * x^n) * x^m
==> x^(n+1) * x^m
\end{spec}
and we are done.
Or are we? What if, in the definition of |(^)|, |x| or |n|
is \emph{negative}? Since a negative integer is not a natural number,
we could dispense with the problem by saying that these situations
fall beyond the bounds of the property we are trying to prove. But
let's look a little closer. If |x| is negative, the property we
are trying to prove still holds (why?). But if |n| is negative,
|x^n| will not terminate (why?). As diligent programmers we may
wish to defend against the latter situation by writing:
\begin{spec}
(^) :: Integer -> Integer -> Integer
x^0 = 1
x^n | n<0 = error "negative exponent"
| otherwise = x * x^(n-1)
\end{spec}
If we consider non-terminating computations and ones that produce an
error to both have the same value, namely |bottom|, then these two
versions of |(^)| are equivalent. Pragmatically, however, the
latter is clearly superior.
Note that the above definition will test for |n<0| on every
recursive call, when actually the only call in which it could happen
is the first. Therefore a slightly more efficient version of this
program would be:
\begin{spec}
(^) :: Integer -> Integer -> Integer
x^n | n<0 = error "negative exponent"
| otherwise = f x n
where f x 0 = 1
f x n = x * f x (n-1)
\end{spec}
Proving the property stated earlier for this version of the program is
straightforward, with one minor distinction: what we really need to
prove is that the property is true for |f|; that is:
\[(\forall x, n\geq0, m\geq0)\ \ |f x (n+m) = f x n * f x m|\]
from which the proof for the whole function follows trivially.
\subsection{A More Efficient Exponentiation Function}
\index{efficiency}
But in fact there is a more serious inefficiency in our exponentiation
function: we are not taking advantage of the fact that, for any even
number $n$, $x^n = (x*x)^{n/2}$. Using this fact, here is a more
clever way to accomplish the exponentiation task, using the names
|(^!)| and |ff| for our functions to distinguish them from the
previous versions:
\begin{spec}
(^!) :: Integer -> Integer -> Integer
x^!n | n<0 = error "negative exponent"
| otherwise = ff x n
where ff x n | n==0 = 1
| even n = ff (x*x) (n `quot` 2)
| otherwise = x * ff x (n-1)
\end{spec}
\syn{\indexwdhs{quot} is Haskell's \emph{quotient} operator, which
returns the integer quotient of the first argument divided by the
second, rounded toward zero.}
You should convince yourself that, intuitively at least, this version
of exponentiation is not only correct, but also more efficient. More
precisely, |(^)| executes a number of steps proportional to |n|,
whereas |(^!)| executes a number of steps proportional to the
$\log_2$ of |n|. The Standard Prelude defines |(^)| similarly
to the way in which |(^!)| is defined here.
Since intuition is not always reliable, let's \emph{prove} that this
version is equivalent to the old. That is, we wish to prove that
|x^n = x^!n| for all |x| and |n|.
A quick look at the two definitions reveals that what we really need
to prove is that |f x n = ff x n|, from which it follows
immediately that |x^n = x^!n|. We do this by induction on |n|,
beginning with the base case |n=0|:
\begin{spec}
f x 0 ==> 1 ==> ff x 0
\end{spec}
so the base case holds trivially. The induction step, however, is
considerably more complicated. We must consider two cases: |n+1|
is either even, or it is odd. If it is odd, we can show that:
\begin{spec}
f x (n+1)
==> x * f x n
==> x * ff x n
==> ff x (n+1)
\end{spec}
and we are done (note the use of the induction hypothesis in the
second step).
If |n+1| is even, we might try proceeding in a similar way:
\begin{spec}
f x (n+1)
==> x * f x n
==> x * ff x n
\end{spec}
But now what shall we do? Since |n| is odd, we might try
unfolding the call to |ff|:
\begin{spec}
x * ff x n
==> x * (x * ff x (n-1))
\end{spec}
but this does not seem to be getting us anywhere. Furthermore,
\emph{folding} the call to |ff| (as we did in the odd case) would
involve \emph{doubling} |n| and taking the square root of |x|,
neither of which seems like a good idea!
We could also try going in the other direction:
\begin{spec}
ff x (n+1)
==> ff (x*x) ((n+1) `quot` 2)
==> f (x*x) ((n+1) `quot` 2)
\end{spec}
The use of the induction hypothesis in the second step needs to be
justified, because the first argument to |f| has changed from
|x| to |x*x|. But recall that the induction hypothesis states
that for \emph{all} values |x|, and all natural numbers up to |n|,
|f x n| is the same as |ff x n|. So this is OK.
But even allowing this, we seem to be stuck again!
Instead of pushing this line of reasoning further, let's pursue a
different tact based on the (valid) assumption that if |m| is even,
then:
\[ |m = m `quot` 2 + m `quot` 2| \]
Let's use this fact together with the property that we proved in the
last section:
\begin{spec}
f x (n+1)
==> f x ((n+1) `quot` 2 + (n+1) `quot` 2)
==> f x ((n+1) `quot` 2) * f x ((n+1) `quot` 2)
\end{spec}
Next, as with the proof in the last section involving |reverse|,
let's make an assumption about a property that will help us along.
Specifically, what if we could prove that |f x n * f x n| is equal
to |f (x*x) n|? If so, we could proceed as follows:
\begin{spec}
f x ((n+1) `quot` 2) * f x ((n+1) `quot` 2)
==> f (x*x) ((n+1) `quot` 2)
==> ff (x*x) ((n+1) `quot` 2)
==> ff x (n+1)
\end{spec}
and we are finally done. Note the use of the induction hypothesis in
the second step, as justified earlier. The proof of the auxiliary
property is not difficult, but also requires induction; it is shown in
Figure \ref{fig:exp-lemma}.
\begin{figure}
\cbox{
\begin{minipage}{4.75in}
Base case (|n=0|):
\begin{spec}
f x 0 * f x 0
==> 1 * 1
==> 1
==> f (x*x) 0
\end{spec}
Induction step (|n+1|):
\begin{spec}
f x (n+1) * f x (n+1)
==> (x * f x n) * (x * f x n)
==> (x*x) * (f x n * f x n)
==> (x*x) * f (x*x) n
==> f (x*x) (n+1)
\end{spec}
\end{minipage}}
\caption{Proof that |f x n * f x n = f (x*x) n|.}
\label{fig:exp-lemma}
\end{figure}
Aside from improving efficiency, one of the pleasant outcomes of
proving that |(^)| and |(^!)| are equivalent is that
\emph{anything that we prove about one function will be true for the
other}. For example, the validity of the property that we proved
earlier:
\[ |x^(n+m) = x^n * x^m| \]
immediately implies the validity of:
\[ |x^!(n+m) = x^!n * x^!m| \]
Although |(^!)| is more efficient than |(^)|, it is also more
complicated, so it makes sense to try proving new properties for
|(^)|, since the proofs will likely be easier.
The moral of this story is that you should not throw away old code that
is simpler but less efficient than a newer version. That old code can
serve at least two good purposes: First, if it is simpler, it is
likely to be easier to understand, and thus serves a useful role in
documenting your effort. Second, as we have just discussed, if it is
provably equivalent to the new code, then it can be used to simplify
the task of proving properties about the new code.
\vspace{.1in}\hrule
\begin{exercise}\em
The function |(^!)| can be made more efficient by noting that in
the last line of the definition of |ff|, |n| is odd, and
therefore |n-1| must be even, so the test for |n| being even on
the next recursive call could be avoided. Redefine |(^!)| so that
it avoids this (minor) inefficiency.
\end{exercise}
\begin{exercise}\em
Consider this definition of the \emph{factorial} function:\footnote{The
factorial function is defined mathematically as:
\[\mathit{factorial}(n) =
\left\{ \begin{array}{ll}
1 & \mbox{if $n=0$} \\
n * \mathit{factorial} (n-1) & \mbox{otherwise}
\end{array}
\right.
\] }
\begin{spec}
fac1 :: Integer -> Integer
fac1 0 = 1
fac1 n = n * fac1 (n-1)
\end{spec}
and this alternative definition that uses an ``accumulator:''
\begin{spec}
fac2 :: Integer -> Integer
fac2 n = fac' n 1
where fac' 0 acc = acc
fac' n acc = fac' (n-1) (n*acc)
\end{spec}
Prove that |fac1 = fac2|.
\end{exercise}
\vspace{.1in}\hrule
\out{
There are several points that you should remember about this proof process:
\begin{enumerate}
\item Two programs can be proved equivalent using induction.
\item It is often the case that auxiliary properties are needed to
prove certain properties, whose proofs in turn can be treated
separately.
\item The most obvious proof strategy is not always best.
\end{enumerate}
Indeed, the full definition of |(^)| as given in the Standard
Prelude is:
\begin{spec}
(^) :: (Num a, Integral b) => a -> b -> a
x ^ 0 = 1
x ^ n | n > 0 = f x (n-1) x
where f _ 0 y = y
f x n y = g x n
where g x n | even n = g (x*x) (n `quot` 2)
| otherwise = f x (n-1) (x*y)
_ ^ _ = error "Prelude.^: negative exponent"
\end{spec}
}