From 286a98aeb02ee44311b8007a10b8329bbf986577 Mon Sep 17 00:00:00 2001 From: Andrea Asperti Date: Wed, 27 Feb 2013 16:48:15 +0000 Subject: [PATCH] Chapter 1 revisited --- matita/matita/lib/tutorial/chapter1.ma | 184 +++++++++++++++++-------- 1 file changed, 124 insertions(+), 60 deletions(-) diff --git a/matita/matita/lib/tutorial/chapter1.ma b/matita/matita/lib/tutorial/chapter1.ma index e726eac07..f2c70a238 100644 --- a/matita/matita/lib/tutorial/chapter1.ma +++ b/matita/matita/lib/tutorial/chapter1.ma @@ -1,18 +1,16 @@ -(* -Data types, functions and theorems +(****************** Data types, functions and theorems ************************) -Matita is both a programming language and a theorem proving environment: -you define datatypes and programs, and then prove properties on them. -Very few things are built-in: not even booleans or logical connectives -(but you may of course use libraries, as in normal programming languages). -The main philosophy of the system is to let you define your own data-types -and functions using a powerful computational mechanism based on the -declaration of inductive types. +(* Matita is both a programming language and a theorem proving environment: you +define datatypes and programs, and then prove properties on them. +Very few things are built-in: not even booleans or logical connectives (but you +may of course use libraries, as in normal programming languages). +The main philosophy of the system is to let you define your own data-types and +functions using a powerful computational mechanism based on the declaration of +inductive types. Let us start this tutorial with a simple example based on the following well -known problem. +known problem: the goat, the wolf and the cabbage. -The goat, the wolf and the cabbage A farmer need to transfer a goat, a wolf and a cabbage across a river, but there is only one place available on his boat. Furthermore, the goat will eat the cabbage if they are left alone on the same bank, and similarly the wolf will eat @@ -28,11 +26,40 @@ few preliminary notions not worth discussing for the moment. include "basics/logic.ma". -inductive bank: Type[0] ≝ +inductive bank: Type[0] ≝ | east : bank | west : bank. -(* We can now define a simple function computing, for each bank of the river, the +(* The definition starts with the keyword "inductive" followed by the name we +want to give to the new datatype (in this case, "bank"), followed by its type (a +type of a type is traditionally called a "sort" in type theory). We have +basically two sorts in Matita: "Prop" and "Type". "Prop" is meant for logical +propositions, while "Type" should be used for dataypes (we shall tell more about +the attribute $0$ in a later Section). + +The definition proceeds with the keyword ``:='' (or "\def") followed by the +"body" of the definition. The body is just a list of "constructors" for the +inductive type, separated by the keyword | (vertical bar). Each constructor is a +pair composed by a name and its type. Constructors (in our case, "east" and +"west") are the "canonical inhabitants" of the inductive type we are defining +(in our case, "bank"), hence their type must be of type "bank". +In general, as we shall see, constructors for an inductive type T may have a +more complex structure, and in particular can be recursive: the general proviso +is that they must always "return" a result of type T. +Hence, the declaration of a constructor c for and inductive type T has the +following typical shape: + A1 → … → An → T +where A1 … A_n can be arbitrary types, comprising T itself. Not every form of +recursive constructors is accepted however: in order to ensure logical +consistency, we must respect some positivity conditions, that we shall discuss +in Section ??. + +As a general rule, the inductive type must be conceptually understood as the +smallest collection of terms freely generated by its constructors. *) + +(********************************* functions **********************************) + +(* We define a first simple function computing, for each bank of the river, the opposite one. *) definition opposite ≝ λs. @@ -41,9 +68,49 @@ match s with | west ⇒ east ]. +(* Non recursive functions must be defined in Matita using the keyword +"definition" followed by the name of the function. The type of the function is +optional, since in many cases it can be automatically inferred by the system. +The definition proceeds with the keyword ``:='' followed by the function body. +The body starts with a list of "input parameters", preceded by the symbol λ +(\lambda); many latex-like macro are automatically converted by Matita into +Unicode symbols: see \verb+View >Tex\UTF-8 table+ in the menu bar for a complete +list. + +The definition proceeds by "pattern matching" on the parameter s: if the input +bank is east we return west, and conversely if it is west we return east. + +Pattern matching is a well known feature typical of functional programming +(especially of the ML family), allowing simple access to the components of +complex data structures. A function definition most often corresponds to pattern +matching over one or more of its parameters, allowing the function to be easily +defined by cases. + +The syntactic structure of a match expression is the following: + +match expr with + [ p1 => expr1 + | p2 => expr1 + : + | pn => exprn + ] + +The expression expr, that is supposed to be an element of an inductive type T, +is matched sequentially to the various patterns p1, ..., pn. A pattern pi is +just a constructor of the inductive type T possibly applied to a list of +variables, bound inside the corresponding branch expri. +If the pattern pi matches the value expr, then the corresponding branch expri is +evaluated (after replacing in it the pattern variables with the corresponding +subterms of expr). +Usually, all expressions expr have a same, uniform type; since however Matita +supports dependent types, the type of branches could depend on the matched +expression, too. *) + +(******************************* A first lemma ********************************) + (* Functions are live entities, and can be actually computed. To check this, let -us state the property that the opposite bank of east is west; every lemma needs a -name for further reference, and we call it "east_to_west". *) +us state the property that the opposite bank of east is west; every lemma needs +a name for further reference, and we call it "east_to_west". *) lemma east_to_west : opposite east = west. @@ -87,13 +154,17 @@ lemma performing some book-keeping operations. In this case, we avoid the unnecessary simplification step: // will take care of it. *) -lemma west_to_east : opposite west = east. +lemma west_to_east: opposite west = east. // qed. -(* -Introduction +(* Instead of "lemma", you may also use "theorem" or "corollary". Matita does +not attempt to make a semantic distinction between them, and their use is +entirely up to the user. *) + +(********************************* Introduction *******************************) -A slightly more complex problem consists in proving that opposite is idempotent *) +(* A slightly more complex problem consists in proving that opposite is +idempotent *) lemma idempotent_opposite : ∀x. opposite (opposite x) = x. @@ -121,7 +192,7 @@ the name of the hypothesis (more generally, an arbitrary expression) that must b the object of the case analysis (in our case, b). *) -cases b +cases b (* Executing the previous command has the effect of opening two subgoals, corresponding to the two cases b=east and b=west: you may switch from one to the @@ -133,10 +204,9 @@ them in turn, in a way that will be described at the end of this section. // qed. -(* -Predicates +(********************************** Predicates ********************************) -Instead of working with functions, it is sometimes convenient to work with +(*I nstead of working with functions, it is sometimes convenient to work with predicates. For instance, instead of defining a function computing the opposite bank, we could declare a predicate stating when two banks are opposite to each other. Only two cases are possible, leading naturally to the following @@ -150,8 +220,8 @@ inductive opp : bank → bank → Prop ≝ (* In precisely the same way as "bank" is the smallest type containing east and west, opp is the smallest predicate containing the two sub-cases east_west and weast_east. If you have some familiarity with Prolog, you may look at opp as the -predicate defined by the two clauses - in this case, the two facts - ast_west and -west_east. +predicate defined by the two clauses - in this case, the two facts - ast_west +and west_east. Between opp and opposite we have the following relation: opp a b iff a = opposite b @@ -170,10 +240,9 @@ automation *) cases oppab // qed. -(* -Rewriting +(******************************** Rewriting ***********************************) -Let us come to the opposite direction. *) +(* Let us come to the opposite direction. *) lemma opposite_to_opp: ∀a,b. a = opposite b → opp a b. @@ -192,10 +261,9 @@ opposite b into a, we would have typed "