]> matita.cs.unibo.it Git - helm.git/blob - matita/matita/lib/tutorial/chapter2.ma
adding tutorial
[helm.git] / matita / matita / lib / tutorial / chapter2.ma
1 (**************************************************************************)
2 (*       ___                                                              *)
3 (*      ||M||                                                             *)
4 (*      ||A||       A project by Andrea Asperti                           *)
5 (*      ||T||                                                             *)
6 (*      ||I||       Developers:                                           *)
7 (*      ||T||         The HELM team.                                      *)
8 (*      ||A||         http://helm.cs.unibo.it                             *)
9 (*      \   /                                                             *)
10 (*       \ /        This file is distributed under the terms of the       *)
11 (*        v         GNU General Public License Version 2                  *)
12 (*                                                                        *)
13 (**************************************************************************)
14
15 include "basics/types.ma".
16
17 (* Most of the types we have seen so far are enumerated types, composed by a 
18 finite set of alternatives, and records, composed by tuples of heteregoneous 
19 elements. A more interesting case of type definition is when some of the rules 
20 defining its elements are recursive, i.e. they allow the formation of more 
21 elements of the type in terms of the already defined ones. The most typical case 
22 is provided by the natural numbers, that can be defined as the smallest set 
23 generated by a constant 0 and a successor function from natural numbers to natural
24 numbers *)
25
26 inductive nat : Type[0] ≝ 
27 | O :nat
28 | S: nat →nat.
29
30 (* The two terms O and S are called constructors: they define the signature of the
31 type, whose objects are the elements freely generated by means of them. So, 
32 examples of natural numbers are O, S O, S (S O), S (S (S O)) and so on. 
33
34 The language of Matita allows the definition of well founded recursive functions 
35 over inductive types; in order to guarantee termination of recursion you are only 
36 allowed to make recursive calls on structurally smaller arguments than the ones 
37 you received in input. Most mathematical functions can be naturally defined in this
38 way. For instance, the sum of two natural numbers can be defined as follows *)
39
40 let rec add n m ≝ 
41 match n with
42 [ O ⇒ m
43 | S a ⇒ S (add a m)
44 ].
45
46 (*
47 Elimination
48
49 It is worth to observe that the previous algorithm works by recursion over the
50 first argument. This means that, for instance, (add O x) will reduce to x, as 
51 expected, but (add x O) is stuck. 
52 How can we prove that, for a generic x, (add x O) = x? The mathematical tool to do 
53 it is called induction. The induction principle states that, given a property P(n) 
54 over natural numbers, if we prove P(0) and prove that, for any m, P(m) implies P(S m), 
55 than we can conclude P(n) for any n. 
56
57 The elim tactic, allow you to apply induction in a very simple way. If your goal is 
58 P(n), the invocation of
59   elim n
60 will break down your task to prove the two subgoals P(0) and ∀m.P(m) → P(S m).
61
62 Let us apply it to our case *)
63
64 lemma add_0: ∀a. add a O = a.
65 #a elim a
66
67 (* If you stop the computation here, you will see on the right the two subgoals 
68     - add O O = O
69     - ∀x. add x 0 = x → add (S x) O = S x
70 After normalization, both goals are trivial.
71 *)
72
73 normalize // qed.
74
75 (* In a similar way, it is convenient to state a lemma about the behaviour of 
76 add when the second argument is not zero. *)
77
78 lemma add_S : ∀a,b. add a (S b) = S (add a b).
79
80 (* In the same way as before, we proceed by induction over a. *)
81
82 #a #b elim a normalize //
83 qed. 
84
85 (* We are now in the position to prove the commutativity of the sum *)
86
87 theorem add_comm : ∀a,b. add a b = add b a.
88
89 #a elim a normalize
90
91 (* We have two sub goals:
92   G1: ∀b. b = add b O
93   G2: ∀x.(∀b. add x b = add b x) → ∀b. S (add x b) = add b (S x). 
94 G1 is just our lemma add_O. For G2, we start introducing x and the induction 
95 hypothesis IH; then, the goal is proved by rewriting using add_S and IH.
96 For Matita, the task is trivial and we can simply close the goal with // *)
97
98 // qed.
99
100 (* COERCIONS *)
101
102 inductive bool : Type[0] ≝
103 | tt : bool
104 | ff : bool.
105
106 definition nat_of_bool ≝ λb. match b with 
107 [ tt ⇒ S O 
108 | ff ⇒ O 
109 ].
110
111 (* coercion nat_of_bool. ?? *)
112  
113 (* Let us now define the following function: *)
114
115 definition twice ≝ λn.add n n. 
116
117 (* 
118 Existential
119
120 We are interested to prove that for any natural number n there exists a natural 
121 number m that is the integer half of n. This will give us the opportunity to 
122 introduce new connectives and quantifiers and, later on, to make some interesting 
123 consideration on proofs and computations. *)
124
125 theorem ex_half: ∀n.∃m. n = twice m ∨ n = S (twice m).
126 #n elim n normalize
127
128 (* We proceed by induction on n, that breaks down to the following goals:
129   G1: ∃m.O = add O O ∨ O = S (add m m)
130   G2: ∀x.(∃m. x = add m m ∨ x = S (add m m))→ ∃m. S x = add m m ∨ S x = S (add m m)
131 The only way we have to prove an existential goal is by exhibiting the witness, 
132 that in the case of first goal is O. We do it by apply the term called ex_intro 
133 instantiated by the witness. Then, it is clear that we must follow the left branch 
134 of the disjunction. One way to do it is by applying the term or_introl, that is 
135 the first constructor of the disjunction. However, remembering the names of 
136 constructors can be annyoing: we can invoke the application of the n-th 
137 constructor of an inductive type (inferred by the current goal) by typing %n. At 
138 this point we are left with the subgoal O = add O O, that is closed by 
139 computation. It is worth to observe that invoking automation at depth /3/ would 
140 also automatically close G1.
141 *)
142
143   [@(ex_intro … O) %1 //
144
145 (* 
146 Destructuration
147
148 The case of G2 is more complex. We should start introducing x and the 
149 inductive hypothesis
150      IH: ∃m. x = add m m ∨ x = S (add m m) 
151 At this point we should assume the existence of m enjoying the inductive 
152 hypothesis. To eliminate the existential from the context we can just use the 
153 case tactic. This situation where we introduce something into the context and 
154 immediately eliminate it by case analysis is so frequent that Matita provides a 
155 convenient shorthand: you can just type a single "*". 
156 The star symbol should be reminiscent of an explosion: the idea is that you have
157 a structured hypothesis, and you ask to explode it into its constituents. In the 
158 case of the existential, it allows to pass from a goal of the shape 
159     (∃x.P x) → Q
160 to a goal of the shape
161     ∀x.P x → Q
162 *)
163   |#x * 
164 (* At this point we are left with a new goal with the following shape
165   G3: ∀m. x = add m m ∨ x = S (add m m) → ....  
166 We should introduce m, the hypothesis H: x = add m m ∨ x = S (add m m), and 
167 then reason by cases on this hypothesis. It is the same situation as before: 
168 we explode the disjunctive hypothesis into its possible consituents. In the case 
169 of a disjunction, the * tactic allows to pass from a goal of the form
170     A∨B → Q
171 to two subgoals of the form
172     A → Q  and  B → Q
173 *)
174   #m * #eqx
175 (* In the first subgoal, we are under the assumption that x = add m m. The half 
176 of (S x) is hence m, and we have to prove the right branch of the disjunction. 
177 In the second subgoal, we are under the assumption that x = S (add m m). The halh 
178 of (S x) is hence (S m), and have to follow the left branch of the disjunction.
179 *)
180   [@(ex_intro … m) /2/ | @(ex_intro … (S m)) normalize /2/
181   ]
182 qed. 
183
184 (* 
185 Computing vs. Proving
186
187 Instead of proving the existence of a number corresponding to the half of n, 
188 we could be interested in computing it. The best way to do it is to define this 
189 division operation together with the remainder, that in our case is just a 
190 boolean value: tt if the input term is even, and ff if the input term is odd. 
191 Since we must return a pair, we could use a suitably defined record type, or 
192 simply a product type nat × bool, defined in the basic library. The product type 
193 is just a sort of general purpose record, with standard fields fst and snd, called 
194 projections. 
195 A pair of values n and m is written (pair … m n) or \langle n,m \rangle - visually 
196 rendered as 〈n,m〉 
197
198 We first write down the function, and then discuss it.*)
199
200 let rec div2 n ≝ 
201 match n with
202 [ O ⇒ 〈O,ff〉
203 | S a ⇒ 
204    let 〈q,r〉 ≝ (div2 a) in
205    match r with
206    [ tt ⇒ 〈S q,ff〉 
207    | ff ⇒ 〈q, tt〉
208    ]
209 ]. 
210
211 (* The function is computed by recursion over the input n. If n is 0, then the 
212 quotient is 0 and the remainder is tt. If n = S a, we start computing the half 
213 of a, say 〈q,b〉. Then we have two cases according to the possible values of b: 
214 if b is tt, then we must return 〈q,ff〉, while if b = ff then we must return 
215 〈S q,tt〉.
216
217 It is important to point out the deep, substantial analogy between the algorithm 
218 for computing div2 and the the proof of ex_half. In particular ex_half returns a 
219 proof of the kind ∃n.A(n)∨B(n): the really informative content in it is the 
220 witness n and a boolean indicating which one between the two conditions A(n) and 
221 B(n) is met. This is precisely the quotient-remainder pair returned by div2.
222 In both cases we proceed by recurrence (respectively, induction or recursion) over 
223 the input argument n. In case n = 0, we conclude the proof in ex_half by providing 
224 the witness O and a proof of A(O); this corresponds to returning the pair 〈O,ff〉 in 
225 div2. Similarly, in the inductive case n = S a, we must exploit the inductive 
226 hypothesis for a (i.e. the result of the recursive call), distinguishing two subcases 
227 according to the the two possibilites A(a) or B(a) (i.e. the two possibile values of 
228 the remainder for a). The reader is strongly invited to check all remaining details.
229
230 Let us now prove that our div2 function has the expected behaviour.
231 *)
232
233 lemma surjective_pairing: ∀A,B.∀p:A×B. p = 〈fst … p,\snd … p〉.
234 #A #B * // qed.
235
236 lemma div2SO: ∀n,q. div2 n = 〈q,ff〉 → div2 (S n) = 〈q,tt〉.
237 #n #q #H normalize >H normalize // qed.
238
239 lemma div2S1: ∀n,q. div2 n = 〈q,tt〉 → div2 (S n) = 〈S q,ff〉.
240 #n #q #H normalize >H normalize // qed.
241
242 lemma div2_ok: ∀n,q,r. div2 n = 〈q,r〉 → n = add (twice q) (nat_of_bool r).
243 #n elim n
244   [#q #r normalize #H destruct //
245   |#a #Hind #q #r 
246    cut (div2 a = 〈fst … (div2 a), snd … (div2 a)〉) [//] 
247    cases (snd … (div2 a))
248     [#H >(div2S1 … H) #H1 destruct 
249      >add_0 normalize @eq_f 
250      cut (∀n.add (twice n) (S O) = add n (S n))
251      [|#Hcut <Hcut @(Hind … H)] #m normalize >add_S //
252      (* >add_S whd in ⊢ (???%); <add_S @(Hind … H) *)
253     |#H >(div2SO … H) #H1 destruct >add_S @eq_f @(Hind … H) 
254     ]
255 qed.
256
257 (* 
258 Mixing proofs and computations
259
260 There is still another possibility, however, namely to mix the program and its 
261 specification into a single entity. The idea is to refine the output type of the 
262 div2 function: it should not be just a generic pair 〈q,r〉 of natural numbers but a 
263 specific pair satisfying the specification of the function. In other words, we need 
264 the possibility to define, for a type A and a property P over A, the subset type 
265 {a:A|P(a)} of all elements a of type A that satisfy the property P. Subset types 
266 are just a particular case of the so called dependent types, that is types that 
267 can depend over arguments (such as arrays of a specified length, taken as a 
268 parameter).These kind of types are quite unusual in traditional programming 
269 languages, and their study is one of the new frontiers of the current research on 
270 type systems. 
271
272 There is nothing special in a subset type {a:A|P(a)}: it is just a record composed 
273 by an element of a of type A and a proof of P(a). The crucial point is to have a 
274 language reach enough to comprise proofs among its expressions. 
275 *)
276
277 record Sub (A:Type[0]) (P:A → Prop) : Type[0] ≝
278   {witness: A; 
279    proof: P witness}.
280
281 definition qr_spec ≝ λn.λp.∀q,r. p = 〈q,r〉 → n = add (twice q) (nat_of_bool r).
282   
283 (* We can now construct a function from n to {p|qr_spec n p} by composing the objects
284 we already have *)
285
286 definition div2P: ∀n. Sub (nat×bool) (qr_spec n) ≝ λn.
287  mk_Sub ?? (div2 n) (div2_ok n).
288
289 (* But we can also try do directly build such an object *)
290
291 definition div2Pagain : ∀n.Sub (nat×bool) (qr_spec n).
292 #n elim n
293   [@(mk_Sub … 〈O,ff〉) normalize #q #r #H destruct //
294   |#a * #p #qrspec 
295    cut (p = 〈fst … p, snd … p〉) [//] 
296    cases (snd … p)
297     [#H @(mk_Sub … 〈S (fst … p),ff〉) whd #q #r #H1 destruct @eq_f >add_S
298      whd in ⊢ (???%); <add_S @(qrspec … H)
299     |#H @(mk_Sub … 〈fst … p,tt〉) whd #q #r #H1 destruct >add_S @eq_f @(qrspec … H) 
300   ]
301 qed.
302
303 example full7: (div2Pagain (S(S(S(S(S(S(S O)))))))) = ?.
304 [normalize
305 // qed.
306
307 example quotient7: witness … (div2Pagain (S(S(S(S(S(S(S O)))))))) = ?.
308 normalize 〈S(S(S O)),tt〉.
309 normalize // qed.
310
311 example quotient8: witness … (div2Pagain (twice (twice (twice (twice (S O)))))) 
312        = 〈twice (twice (twice (S O))), ff〉.
313 // qed.