REVISED: Thursday, December 12, 2024
1. INTRODUCTION
ASSOCIATIVITY OF LIST CONCATENATION
A list can be either nil or else cons applied to a number and a list.
Applications of the declared constructors to one another are the only possible shapes that elements of an inductively defined set can have.
This last fact directly gives rise to a way of reasoning about inductively defined sets. A number is either O or S applied to some smaller number. A list is either nil or is cons applied to some number and some smaller list; etc. Thus, if we have in mind some proposition P that mentions a list l and we want to argue that P holds for all lists, we can reason as follows:
First, show that P is true of l when l is nil.
Then show that P is true of l when l is cons n l' for some number n and some smaller list l', assuming that P is true for l'.
Since larger lists can always be broken down into smaller ones, eventually reaching nil, these two arguments establish the truth of P for all lists l.
2. EXAMPLE
(* ++ is constructor for append *)
Theorem app_assoc : forall l1 l2 l3 : list Type,
(l1 ++ l2) ++ l3 = l1 ++ (l2 ++ l3).
Proof.
intros l1 l2 l3. induction l1 as [| n l1' IHl1'].
- (* l1 = nil *)
simpl. (* Unfold the ++ operator *)
reflexivity.
- (* l1 = cons n l1' *)
simpl.
rewrite IHl1'.
reflexivity.
Qed.
Compiling:
(* ++ is constructor for append *)
Theorem app_assoc : forall l1 l2 l3 : list Type,
(l1 ++ l2) ++ l3 = l1 ++ (l2 ++ l3).
The Goal window of CoqIDE shows:
1 goal
A, B, C : Prop
______________________________________(1/1)
forall l1 l2 l3 : list Type,
(l1 ++ l2) ++ l3 = l1 ++ l2 ++ l3
Theorem Declaration:
Theorem app_assoc : ... : Declares a theorem named app_assoc.
forall l1 l2 l3 : list Type,: Specifies that the theorem holds for any three lists of any type.
(l1 ++ l2) ++ l3 = l1 ++ (l2 ++ l3).: States the property to be proven: associativity of list concatenation.
Compiling:
Proof.
The Goal window of CoqIDE shows:
1 goal
A, B, C : Prop
______________________________________(1/1)
forall l1 l2 l3 : list Type,
(l1 ++ l2) ++ l3 = l1 ++ l2 ++ l3
Proof Initiation:
Proof.: Starts the proof.
Compiling:
intros l1 l2 l3.
The Goal window of CoqIDE shows:
1 goal
A, B, C : Prop
l1 : list Type
l2 : list Type
l3 : list Type
______________________________________(1/1)
(l1 ++ l2) ++ l3 = l1 ++ l2 ++ l3
Induction Hypothesis:
intros l1 l2 l3.: Introduces the variables l1, l2, and l3 for the three lists.
Compiling:
induction l1 as [| n l1' IHl1'].
The Goal window of CoqIDE shows:
2 goals
A, B, C : Prop
l2 : list Type
l3 : list Type
______________________________________(1/2)
([] ++ l2) ++ l3 = [] ++ l2 ++ l3
______________________________________(2/2)
((n :: l1') ++ l2) ++ l3 = (n :: l1') ++ l2 ++ l3
induction l1 as [| n l1' IHl1'].: Inducts on the first list l1.
Base Case: l1 = nil
Inductive Case: l1 = cons n l1' with the induction hypothesis IHl1'.
Compiling:
- (* l1 = nil *)
simpl. (* Unfold the ++ operator *)
The Goal window of CoqIDE shows:
1 goal
A, B, C : Prop
l2 : list Type
l3 : list Type
______________________________________(1/1)
l2 ++ l3 = l2 ++ l3
Base Case:
simpl.: Simplifies the expression by unfolding the ++ operator.
Compiling:
reflexivity.
The Goal window of CoqIDE shows:
This subproof is complete, but there are some unfocused goals:
______________________________________(1/1)
((n :: l1') ++ l2) ++ l3 = (n :: l1') ++ l2 ++ l3
reflexivity.: Directly proves the equality by reflexivity, as both sides reduce to the same expression.
Compiling:
- (* l1 = cons n l1' *)
simpl.
The Goal window of CoqIDE shows:
1 goal
A, B, C : Prop
n : Type
l1' : list Type
l2 : list Type
l3 : list Type
IHl1' : (l1' ++ l2) ++ l3 = l1' ++ l2 ++ l3
______________________________________(1/1)
((n :: l1') ++ l2) ++ l3 = (n :: l1') ++ l2 ++ l3
Inductive Case:
simpl.: Simplifies the expression, again unfolding the ++ operator.
Compiling:
rewrite IHl1'.
The Goal window of CoqIDE shows:
1 goal
A, B, C : Prop
n : Type
l1' : list Type
l2 : list Type
l3 : list Type
IHl1' : (l1' ++ l2) ++ l3 = l1' ++ l2 ++ l3
______________________________________(1/1)
n :: l1' ++ l2 ++ l3 = n :: l1' ++ l2 ++ l3
rewrite IHl1'.: Applies the induction hypothesis IHl1' to simplify the expression further.
Compiling:
reflexivity.
The Goal window of CoqIDE shows:
No more goals.
reflexivity.: After simplification, the two sides of the equation become identical, allowing for the application of reflexivity.
Compiling:
Qed.
3. CONCLUSION
Induction: The proof uses mathematical induction on the structure of the first list.
Simplification: The simpl tactic is used to unfold definitions and reduce expressions.
Reflexivity: The reflexivity tactic is used to prove equalities directly.
Induction Hypothesis: The IHl1' represents the induction hypothesis, which is the assumption that the property holds for smaller lists.
By following these steps and applying the appropriate tactics, Coq can verify the correctness of the app_assoc theorem.
The core idea of inductive proofs is to break down a complex problem into simpler instances and then use the inductive hypothesis to solve the simpler instances. In this case, we broke down the problem for cons n l1' into a simpler problem for l1' and then used the inductive hypothesis to solve the simpler problem.
4. REFERENCES
Bird, R. (2015). Thinking Functionally with Haskell. Cambridge, England: Cambridge University Press.
Davie, A. (1992). Introduction to Functional Programming Systems Using Haskell. Cambridge, England: Cambridge University Press.
Goerzen, J. & O'Sullivan, B. & Stewart, D. (2008). Real World Haskell. Sebastopol, CA: O'Reilly Media, Inc.
Hutton, G. (2007). Programming in Haskell. New York: Cambridge University Press.
Lipovača, M. (2011). Learn You a Haskell for Great Good!: A Beginner's Guide. San Francisco, CA: No Starch Press, Inc.
Pierce, Benjamin C., et al., editors. Logical Foundations. Vol. 1 of Software Foundations. Electronic Textbook, 2024. Version 6.7,
http://softwarefoundations.cis.upenn.edu.
Thompson, S. (2011). The Craft of Functional Programming. Edinburgh Gate, Harlow, England: Pearson Education Limited.