]> matita.cs.unibo.it Git - helm.git/blobdiff - matita/matita/lib/tutorial/chapter12.ma
update in standard library
[helm.git] / matita / matita / lib / tutorial / chapter12.ma
index c0d8f8ae8d186791a7b9d0a67688cc1e0fa614be..07a5fd19f04586d76a2bfa627ede33b789a69c9d 100644 (file)
-(*
-Coinductive Types and Predicates
-*)
-
-include "arithmetics/nat.ma".
+(**************************************************************************)
+(*       ___                                                              *)
+(*      ||M||                                                             *)
+(*      ||A||       A project by Andrea Asperti                           *)
+(*      ||T||                                                             *)
+(*      ||I||       Developers:                                           *)
+(*      ||T||         The HELM team.                                      *)
+(*      ||A||         http://helm.cs.unibo.it                             *)
+(*      \   /                                                             *)
+(*       \ /        This file is distributed under the terms of the       *)
+(*        v         GNU General Public License Version 2                  *)
+(*                                                                        *)
+(**************************************************************************)
+
+include "basics/relations.ma".
 include "basics/types.ma".
-include "basics/lists/list.ma".
-
-(* The only primitive data types of Matita are dependent products and universes.
-So far every other user defined data type has been an inductive type. An
-inductive type is declared by giving its list of constructors (or introduction
-rules in the case of predicates). An inhabitant of an inductive type is obtained
-composing together a finite number of constructors and data of other types,
-according to the type of the constructors. Therefore all inhabitants of inductive
-types are essentially finite objects. Natural numbers, lists, trees, states of
-a DFA, letters of an alphabet are all finite and can be defined inductively.
-
-If you think of an inhabitant of an inductive type as a tree of a certain shape,
-whose nodes are constructors, the only trees can be represented are trees of
-finite height. Note, however, that it is possible to have trees of infinite
-width by putting in the argument of a constructor of a type I an enumeration
-of elements of I (e.g. ℕ → I). *)
-
-(* Example of an infinitely branching tree of elements of type A stored in
-the nodes: *)
-inductive infbrtree (A: Type[0]) : Type[0] ≝
-   Nil: infbrtree A
- | Node: A → (ℕ → infbrtree A) → infbrtree A.
-
-(* Example: the tree of natural numbers whose root holds 0 and has as children
-   the leafs 1,2,3,… *)
-example infbrtree_ex ≝ Node ℕ 0 (λn. Node ℕ (S n) (λ_.Nil ℕ)).
-
-(*** Infinite data types via functions ***)
-
-(* In mathematics and less frequently in computer science there is the need to
-also represent and manipulate types of infinite objects. Typical examples are:
-sequences, real numbers (a special case of sequences), data streams (e.g. as
-read from a network interface), traces of diverging computations of a program,
-etc. One possible representation, used in mathematics since a long time, is
-to describe an infinite object by means of an infinite collection of
-approximations (also called observations). Often, the infinite collection can
-be structured in a sequence, identified as a function from the domain of natural
-numbers. *)
-
-(* Example 1: sequences of elements of type A *)
-definition seq : Type[0] → Type[0] ≝ λA:Type[0]. ℕ → A.
-
-(* Example 2: Real numbers as Cauchy sequences and their addition. *)
-(* First we axiomatize the relevant properties of rational numbers. *)
-axiom Q: Type[0].
-axiom Qdist: Q → Q → Q.
-axiom Qleq: Q → Q → Prop.
-interpretation "Qleq" 'leq x y = (Qleq x y).
-axiom transitive_Qleq: transitive … Qleq.
-axiom Qplus: Q → Q → Q.
-interpretation "Qplus" 'plus x y = (Qplus x y).
-axiom Qleq_Qplus:
- ∀qa1,qb1,qa2,qb2. qa1 ≤ qb1 → qa2 ≤ qb2 → qa1 + qa2 ≤ qb1 + qb2.
-axiom Qdist_Qplus:
- ∀qa1,qb1,qa2,qb2. Qdist (qa1 + qa2) (qb1 + qb2) ≤ Qdist qa1 qb1 + Qdist qa2 qb2.
-axiom Qhalve: Q → Q.
-axiom Qplus_Qhalve_Qhalve: ∀q. Qhalve q + Qhalve q = q.
-
-(* A sequence of rationals. *)
-definition Qseq: Type[0] ≝ seq Q.
-
-(* The Cauchy property *)
-definition Cauchy: Qseq → Prop ≝
- λR:Qseq. ∀eps. ∃n. ∀i,j. n ≤ i → n ≤ j → Qdist (R i) (R j) ≤ eps.
-
-(* A real number is an equivalence class of Cauchy sequences. Here we just
-   define the carrier, omitting the necessary equivalence relation for the
-   quotient. *) 
-record R: Type[0] ≝
- { r: Qseq
- ; isCauchy: Cauchy r
- }. 
-
-(* The following coercion is used to write r n to extract the n-th approximation
- from the real number r *)
-coercion R_to_fun : ∀r:R. ℕ → Q ≝ r on _r:R to ?. 
-
-(* Adding two real numbers just requires pointwise addition of the 
- approximations. The proof that the resulting sequence is Cauchy is the standard
- one: to obtain an approximation up to eps it is necessary to approximate both
- summands up to eps/2. The proof that the function is well defined w.r.t. the
- omitted equivalence relation is also omitted. *)
-definition Rplus: R → R → R ≝
- λr1,r2. mk_R (λn. r1 n + r2 n) ….
- #eps
- cases (isCauchy r1 (Qhalve eps)) #n1 #H1
- cases (isCauchy r2 (Qhalve eps)) #n2 #H2
- %{(max n1 n2)} #i #j #K1 #K2 @(transitive_Qleq … (Qdist_Qplus …))
- <(Qplus_Qhalve_Qhalve eps) @Qleq_Qplus [@H1 @le_maxl | @H2 @le_maxr]
- [2,6: @K1 |4,8: @K2]
-qed.
+include "arithmetics/nat.ma".
+include "hints_declaration.ma".
+include "basics/core_notation/invert_1.ma".
 
-(* Example 3: traces of a program *)
-(* Let us introduce a very simple programming language whose instructions
-   can test and set a single implicit variable. *)
-inductive instr: Type[0] ≝
-   p_set: ℕ → instr                 (* sets the variable to a constant *)
- | p_incr: instr                    (* increments the variable *)
- | p_while: list instr → instr.     (* repeats until the variable is 0 *)
-
-(* The status of a program as the values of the variable and the list of
- instructions to be executed. *)
-definition state ≝ ℕ × (list instr).
-
-(* The transition function from a state to the next one. *)
-inductive next: state → state → Prop ≝
-   n_set: ∀n,k,o. next 〈o,(p_set n)::k〉 〈n,k〉
- | n_incr: ∀k,o. next 〈o,p_incr::k〉 〈S o,k〉
- | n_while_exit: ∀b,k. next 〈0,(p_while b)::k〉 〈0,k〉
- | n_while_loop: ∀b,k,o. next 〈S o,(p_while b)::k〉 〈S o,b@(p_while b)::k〉.
-
-(* A diverging trace is a sequence of states such that the n+1-th state is
- obtained executing one step from the n-th state *)
-record div_trace: Type[0] ≝
- { div_tr: seq state
- ; div_well_formed: ∀n. next (div_tr n) (div_tr (S n))
- }.
+(******************* Quotienting in type theory **********************)
 
-(* The previous definition of trace is not very computable: we cannot write
- a program that given an initial state returns its trace. To write that function,
- we first write a computable version of next, called step. *)
-definition step: state → option state ≝
- λs. let 〈o,k〉 ≝ s in
-  match k with
-   [ nil ⇒ None ?
-   | cons hd k ⇒
-      Some … match hd with
-      [ p_set n ⇒ 〈n,k〉
-      | p_incr ⇒ 〈S o,k〉
-      | p_while b ⇒
-         match o with
-         [ O ⇒ 〈0,k〉
-         | S p ⇒ 〈S p,b@(p_while b)::k〉 ]]].
-
-theorem step_next: ∀o,n. step o = Some … n → next o n.
- * #o * [ #n normalize #abs destruct ]
- * normalize
- [ #n #tl * #n' #tl'
- | #tl * #n' #tl'
- | #b #tl * #n' #tl' cases o normalize [2: #r]]
- #EQ destruct //
+(* One fundamental operation in set theory is quotienting: given a set S
+ and an equivalence relation R over S, quotienting creates a new set S/R
+ whose elements are equivalence classes of elements of S. The idea behind
+ quotienting is to replace the structural equality over S with R, therefore
+ identifying elements up to R. The use of equivalence classes is just a
+ technicality.
+ The type theory of Matita does not have quotient types. In the litterature
+ there are alternative proposals to introduce quotient types, but no consensus
+ have been reached yet. Nevertheless, quotient types can be dispensed and
+ replaced by setoids. A setoid is defined as a type S coupled with an
+ equivalence relation R, used to compare elements of S. In place of working
+ with equivalence classes of S up to R, one keeps working with elements of S,
+ but compares them using R in place of =. Setoids over types (elements of
+ Type[0]) can be declared in Matita as follows. *)
+
+record equivalence_relation (A: Type[0]) : Type[0] ≝ {
+    eqrel:> relation A
+  ; equiv_refl: reflexive … eqrel
+  ; equiv_sym: symmetric … eqrel
+  ; equiv_trans: transitive … eqrel
+}.
+
+record setoid : Type[1] ≝ {
+    carrier:> Type[0]
+  ; eq_setoid: equivalence_relation carrier
+}.
+
+(* Note that carrier has been defined as a coercion so that when S is a setoid
+ we can write x:S in place of x: carrier S. *)
+
+(* We use the notation ≃ for the equality on setoid elements. *)
+notation "hvbox(n break ≃ m)"
+  non associative with precedence 45
+for @{ 'congruent $n $m }.
+
+interpretation "eq_setoid" 'congruent n m = (eqrel ? (eq_setoid ?) n m).
+
+(* Example: integers as pairs of naturals (n,m) quotiented by
+   (n1,m1) ≝ (n2,m2) iff n1 + m2 = m1 + n2. The integer +n is represented
+   by any pair (k,n+k), while the integer -n by any pair (n+k,n).
+   The three proof obligations are to prove reflexivity, symmetry and
+   transitivity. Only the latter is not closed by automation. *)
+definition Z: setoid ≝
+ mk_setoid (ℕ × ℕ)
+  (mk_equivalence_relation …
+   (λc1,c2. \fst c1 + \snd c2 = \fst c2 + \snd c1) …).
+ whd // * #x1 #x2 * #y1 #y2 * #z1 #z3 #H1 #H2
+ cut (x1 + y2 + y1 + z3 = y1 + x2 + z1 + y2) // #H3
+ cut ((y2 + y1) + (x1 + z3) = (y2 + y1) + (z1 + x2)) // #H4
+ @(injective_plus_r … H4)
 qed.
 
-theorem next_step: ∀o,n. next o n → step o = Some … n.
- * #o #k * #n #k' #H inversion H normalize
- [ #v #tl #n'
- | #tl #n'
- | #b #tl]
- #EQ1 #EQ2 //
-qed.
-  
-(* Termination is the archetipal undecidable problem. Therefore there is no
- function that given an initial state returns the diverging trace if the program
- diverges or fails in case of error. The best we can do is to give an alternative
- definition of trace that captures both diverging and converging computations. *)
-record trace: Type[0] ≝
- { tr: seq (option state)
- ; well_formed: ∀n,s. tr n = Some … s → tr (S n) = step s
- }.
+(* The two integers (0,1) and (1,2) are equal up to ≃, written
+   〈0,1〉 ≃ 〈1,2〉. Unfolding the notation, that corresponds to
+   eqrel ? (eq_setoid ?) 〈0,1〉 〈1,2〉 which means that the two
+   pairs are to be compare according to the equivalence relation
+   of an unknown setoid ? whose carrier is ℕ × ℕ. An hint can be
+   used to always pick Z as the setoid for ℕ × ℕ. *)
 
-(* The trace is diverging if every state is not final. *)
-definition diverging: trace → Prop ≝
- λt. ∀n. tr t n ≠ None ?.
+unification hint 0 ≔ ;
+    X ≟ Z
+(* ---------------------------------------- *) ⊢
+    ℕ × ℕ ≡ carrier X.
 
-(* The two definitions of diverging traces are equivalent. *)
-theorem div_trace_to_diverging_trace:
- ∀d: div_trace. ∃t: trace. diverging t ∧ tr t 0 = Some … (div_tr d 0).
- #d %{(mk_trace (λn.Some ? (div_tr d n)) …)}
- [2: % // #n % #abs destruct
- | #n #s #EQ destruct lapply (div_well_formed d n) /2 by div_well_formed, next_step/ ]
+(* Thanks to the hint, Matita now accepts the statement. *)
+example ex1: 〈0,1〉 ≃ 〈1,2〉.
+ //
 qed.
 
-theorem diverging_trace_to_div_trace:
- ∀t: trace. diverging t → ∃d: div_trace. tr t 0 = Some … (div_tr d 0).
- #t #H %
- [ % [ #n lapply (H n) -H cases (tr t n) [ * #abs cases (abs …) // ] #s #_ @s
- | #n  lapply (well_formed t n)
-   lapply (H n) cases (tr t n) normalize [ * #abs cases (abs …) // ]
-   * #o #k #_ lapply (H (S n)) -H
-   cases (tr t (S n)) normalize
-   [ * #abs cases (abs …) // ] * #o' #k' #_ #EQ lapply (EQ … (refl …)) -EQ
-     normalize cases k normalize [ #abs destruct ] #hd #tl #EQ destruct -EQ
-     @step_next >e0 // ]
- | lapply (H O) -H cases (tr t O) [ * #abs cases (abs …) // ] // ]
+(* Every type is a setoid when elements are compared up to Leibniz equality. *)
+definition LEIBNIZ: Type[0] → setoid ≝
+ λA.
+  mk_setoid A
+   (mk_equivalence_relation … (eq …) …).
+ //
 qed.
 
-(* However, given an initial state we can always compute a trace.
-   Note that every time the n-th element of the trace is accessed, all the
-   elements in position ≤ n are computed too. *)
-let rec compute_trace_seq (s:state) (n:nat) on n : option state ≝
- match n with
- [ O ⇒ Some … s
- | S m ⇒
-    match compute_trace_seq s m with
-    [ None ⇒ None …
-    | Some o ⇒ step o ]].
-
-definition compute_trace: ∀s:state. Σt:trace. tr t 0 = Some … s.
- #s %
- [ %{(compute_trace_seq s)}
-   #n #o elim n [ whd in ⊢ (??%? → ??%?); #EQ destruct // ]
-   -n #n #_ #H whd in ; whd in ⊢ (??%?); >H //
- | // ]
+(* Note that we declare the hint with a lower precedence level (10 vs 0,
+ precedence levels are in decreasing order). In this way an ad-hoc setoid
+ hint will be always preferred to the Leibniz one. for example,
+ 〈0,1〉 ≃ 〈1,2〉 is still interpreted in Z, while 1 ≃ 2 is interpreted as 1=2. *)
+unification hint 10 ≔ A: Type[0];
+    X ≟ LEIBNIZ A
+(* ---------------------------------------- *) ⊢
+    A ≡ carrier X.
+
+(* Propositions up to coimplication form a setoid. *)
+definition PROP: setoid ≝
+ mk_setoid Prop
+  (mk_equivalence_relation … (λx,y. x ↔ y) …).
+ whd [ @iff_trans | @iff_sym | /2/ ]
 qed.
 
-(*** Infinite data types as coinductive types ***)
-
-(* All the previous examples were handled very easily via sequences
- declared as functions. A few critics can be made to this approach though:
- 1. the sequence allows for random access. In many situations, however, the
-    elements of the sequence are meant to be read one after the other, in
-    increasing order of their position. Moreover, the elements are meant to be
-    consumed (read) linearly, i.e. just once. This suggests a more efficient
-    implementation where sequences are implemented with state machines that
-    emit the next value and enter a new state every time they are read. Indeed,
-    some programming languages like OCaml differentiate (possibly infinite)
-    lists, that are immutable, from mutable streams whose only access operation
-    yields the head and turns the stream into its tail. Data streams read from
-    the network are a typical example of streams: the previously read values are
-    not automatically memoized and are lost if not saved when read. Files on
-    disk are also usually read via streams to avoid keeping all of them in main
-    memory. Another typical case where streams are used is that of diverging
-    computations: in place of generating at once an infinite sequence of values,
-    a function is turned into a stream and asking the next element of the stream
-    runs one more iteration of the function to produce the next output (often
-    an approximation).
- 2. if a sequence computes the n-th entry by recursively calling itself on every
-    entry less than n, accessing the n-th entry requires re-computation of all
-    entries in position ≤ n, which is very inefficient. 
- 3. by representing an infinite object as a collection of approximations, the
-    structure of the object is lost. This was not evident in the previous
-    examples because in all cases the intrinsic structure of the datatype was
-    just that of being ordered and sequences capture the order well. Imagine,
-    however, that we want to represent an infinite binary tree of elements of
-    type A with the previous technique. We need to associate to each position
-    in the infinite tree an element of type A. A position in the tree is itself
-    a path from the root to the node of interest. Therefore the infinite tree
-    is represented as the function (ℕ → 𝔹) → A where 𝔹 are the booleans and the
-    tree structure is already less clear. Suppose now that the binary tree may
-    not be full, i.e. some nodes can have less than two children. Representing
-    such a tree is definitely harder. We may for example use the data type
-    (N → 𝔹) → option A where None would be associated to the position
-    b1 ... bn if a node in such position does not exist. However, we would need
-    to add the invariant that if b1 ... bn is undefined (i.e. assigned to None),
-    so are all suffixes b1 ... bn b_{n+1} ... b_{n+j}.
-
- The previous observations suggest the need for primitive infinite datatypes
- in the language, usually called coinductive types or codata. Matita allows
- to declare coinductive type with the same syntax used for inductive types,
- just replacing the keywork inductive with coinductive. Semantically, the two
- declarations differ because a coinductive type also contains infinite
- inhabitants that respect the typechecking rules.
-*)
+unification hint 0 ≔ ;
+    X ≟ PROP
+(* ---------------------------------------- *) ⊢
+    Prop ≡ carrier X.
+
+(* In set theory functions and relations over a quotient S/R can be defined
+  by lifting a function/relation over S that respects R. Respecting R means that
+  the relations holds for an element x of S iff it holds for every y of S such
+  that x R y. Similarly, a function f respects R iff f x = f y for every x,y
+  such that x R y. In type theory propositions are just special cases of
+  functions whose codomain is Prop. 
+  
+  Note that in the definition of respect for functions in set theory f x and
+  f y are compared using =. When working with setoids in type theory we need
+  to systematically abandon = in favour of ≃, unless we know in advance that
+  a certain type taken in input is never going to be quotiented. We say that
+  a function between two setoids is proper when it respects their equalities. *)
+
+definition proper: ∀I,O:setoid. (I → O) → Prop ≝
+ λI,O,f. ∀x,y:I. x ≃ y → f x ≃ f y.
+(* A proper function is called a morphism. *)
+record morphism (I,O: setoid) : Type[0] ≝ {
+   mor_carr:1> I → O
+ ; mor_proper: proper … mor_carr
+ }.
 
-(* Example 1 revisited: non terminated streams of elements of type A *)
-coinductive streamseq (A: Type[0]) : Type[0] ≝
- sscons: A → streamseq A → streamseq A.
-
-(* Coinductive types can be inhabited by infinite data using coinductive
-   definitions, introduced by the keyword let corec. The syntax of let corec
-   definitions is the same of let rec definitions, but for the declarations
-   of the recursive argument. While let rec definitions are recursive definition
-   that are strictly decreasing on the recursive argument, let corec definitions
-   are productive recursive definitions. A recursive definition is productive
-   if, when fully applied to its arguments, it reduces in a finite amount of time
-   to a constructor of a coinductive type.
-   
-   Let's see some simple examples of coinductive definitions of corecursive
-   streamseqs. *)
-
-(* The streamseq 0 0 0 ...
-   Note that all_zeros is not a function and does not have any argument.
-   The definition is clearly productive because it immediately reduces to
-   the constructor sscons. *)
-let corec all_zeros: streamseq nat ≝ sscons nat 0 all_zeros.
-
-(* The streamseq n (n+1) (n+2) ...
-   The definition behaves like an automaton with state n. When the
-   streamseq is pattern matched, the current value n is returned as head
-   of the streamseq and the tail of the streamseq is the automaton with
-   state (S n). Therefore obtaining the n-th tail of the stream requires O(n)
-   operation, but every further access to its value only costs O(1). Moreover,
-   in the future the implementation of Matita could automatically memoize
-   streams so that obtaining the n-th element would also be an O(1) operation
-   if the same element was previously accessed at least once. This is what
-   is currently done in the implementation of the Coq system for example.
-*)
-let corec from_n (n:ℕ) : streamseq nat ≝ sscons … n (from_n (S n)).
-
-(* In order to retrieve the n-th element from a streamseq we can write a
-   function recursive over n. *)
-let rec streamseq_nth (A: Type[0]) (s: streamseq A) (n:ℕ) on n : A ≝
- match s with [ sscons hd tl ⇒
-  match n with [ O ⇒ hd | S m ⇒ streamseq_nth … tl m ]]. 
-
-(* Any sequence can be turned into the equivalent streamseq and the other
-   way around. *)
-let corec streamseq_of_seq (A: Type[0]) (s: seq A) (n:ℕ) : streamseq A ≝
- sscons … (s n) (streamseq_of_seq A s (S n)).
-
-lemma streamseq_of_seq_ok:
- ∀A:Type[0]. ∀s: seq A. ∀m,n.
-  streamseq_nth A (streamseq_of_seq … s n) m = s (m+n).
- #A #s #m elim m normalize //
-qed.
+(* We introduce a notation for morphism using the symbols of an arrow followed by a dot. *)
+notation "hvbox(I break ⤞ O)"
+  right associative with precedence 20
+for @{ 'morphism $I $O }.
 
-definition seq_of_streamseq: ∀A:Type[0]. streamseq A → seq A ≝ streamseq_nth.
+interpretation "morphism" 'morphism I O = (morphism I O).
 
-lemma seq_of_streamseq_ok:
- ∀A:Type[0]. ∀s: streamseq A. ∀n. seq_of_streamseq … s n = streamseq_nth … s n.
- //
-qed.
+(* By declaring mor_carr as a coercion it is possible to write f x for
+   mor_carr f x in order to apply a morphism f to an argument. *)
 
-(* Example 2 revisited: Real numbers as Cauchy sequences and their addition.
-   We closely follow example 2 replacing sequences with streamseqs.
-*)
+(* Example: opposite of an integer number. We first implement the function
+  over Z and then lift it to a morphism. The proof obligation is to prove
+  properness. *)
+definition opp: Z → Z ≝ λc.〈\snd c,\fst c〉.
 
-definition Qstreamseq: Type[0] ≝ streamseq Q.
-
-definition Qstreamseq_nth ≝ streamseq_nth Q.
-
-(* The Cauchy property *)
-definition Cauchy': Qstreamseq → Prop ≝
- λR:Qstreamseq. ∀eps. ∃n. ∀i,j. n ≤ i → n ≤ j → Qdist (Qstreamseq_nth R i) (Qstreamseq_nth R j) ≤ eps.
-
-(* A real number is an equivalence class of Cauchy sequences. Here we just
-   define the carrier, omitting the necessary equivalence relation for the
-   quotient. *) 
-record R': Type[0] ≝
- { r': Qstreamseq
- ; isCauchy': Cauchy' r'
- }. 
-
-(* The following coercion is used to write r n to extract the n-th approximation
- from the real number r *)
-coercion R_to_fun' : ∀r:R'. ℕ → Q ≝ (λr. Qstreamseq_nth (r' r)) on _r:R' to ?. 
-
-(* Pointwise addition over Qstreamseq defined by corecursion.
-   The definition is productive because, when Rplus_streamseq is applied to
-   two closed values of type Qstreamseq, it will reduce to sscons. *)
-let corec Rplus_streamseq (x:Qstreamseq) (y:Qstreamseq) : Qstreamseq ≝
- match x with [ sscons hdx tlx ⇒
- match y with [ sscons hdy tly ⇒
-  sscons … (hdx + hdy) (Rplus_streamseq tlx tly) ]].
-
-(* The following lemma was for free using sequences. In the case of streamseqs
- it must be proved by induction over the index because Qstreamseq_nth is defined by
- recursion over the index. *)
-lemma Qstreamseq_nth_Rplus_streamseq:
- ∀i,x,y.
-  Qstreamseq_nth (Rplus_streamseq x y) i = Qstreamseq_nth x i + Qstreamseq_nth y i.
- #i elim i [2: #j #IH] * #xhd #xtl * #yhd #ytl // normalize @IH
+definition OPP: Z ⤞ Z ≝ mk_morphism … opp ….
+ normalize //
 qed.
 
-(* The proof that the resulting sequence is Cauchy is exactly the same we
- used for sequences, up to two applications of the previous lemma. *)
-definition Rplus': R' → R' → R' ≝
- λr1,r2. mk_R' (Rplus_streamseq (r' r1) (r' r2)) ….
- #eps
- cases (isCauchy' r1 (Qhalve eps)) #n1 #H1
- cases (isCauchy' r2 (Qhalve eps)) #n2 #H2
- %{(max n1 n2)} #i #j #K1 #K2
- >Qstreamseq_nth_Rplus_streamseq >Qstreamseq_nth_Rplus_streamseq
- @(transitive_Qleq … (Qdist_Qplus …))
- <(Qplus_Qhalve_Qhalve eps) @Qleq_Qplus [@H1 @le_maxl | @H2 @le_maxr]
- [2,6: @K1 |4,8: @K2]
+(* When writing expressions over Z we will always use the function opp, that
+ does not carry additional information. The following hints will be automatically
+ used by the system to retrieve the morphism associated to opp when needed, i.e.
+ to retrieve the proof of properness of opp. This is a use of unification hints
+ to implement automatic proof search. The first hint is used when the function
+ is partially applied, the second one when it is applied to an argument. *)
+
+unification hint 0 ≔ ;
+    X ≟ OPP
+(* ---------------------------------------- *) ⊢
+    opp ≡ mor_carr … X.
+
+unification hint 0 ≔ x:Z ;
+    X ≟ OPP
+(* ---------------------------------------- *) ⊢
+    opp x ≡ mor_carr … X x.
+
+(* Example: we state that opp is proper and we will prove it without using
+ automation and without referring to OPP. When we apply the universal mor_proper
+ property of morphisms, Matita looks for the morphism associate to opp x and
+ finds it thanks to the second unification hint above, completing the proof. *)
+example ex2: ∀x,y. x ≃ y → opp x ≃ opp y.
+ #x #y #EQ @mor_proper @EQ
 qed.
 
-(***** Intermezzo: the dynamics of coinductive data ********)
-
-(* Let corec definitions, like let rec definitions, are a form of recursive
- definition where the definiens occurs in the definiendum. Matita compares
- types up to reduction and reduction always allows the expansion of non recursive
- definitions. If it also allowed the expansion of recursive definitions, reduction
- could diverge and type checking would become undecidable. For example,
- we defined all_zeros as "sscons … 0 all_zeros". If the system expanded all
- occurrences of all_zeros, it would expand it forever to
- "sscons … 0 (sscons … 0 (sscons … 0 …))".
- In order to avoid divergence, recursive definitions are only expanded when a
- certain condition is met. In the case of a let-rec defined function f that is
- recursive on its n-th argument, f is only expanded when it occurs in an
- application (f t1 ... tn ...) and tn is (the application of) a constructor.
- Termination is guaranteed by the combination of this restriction and f being
- guarded by destructors: the application (f t1 ... tn ...) can reduce to a term
- that contains another application (f t1' ... tn' ...) but the size of tn'
- (roughly the number of nested constructors) will be smaller than the size of tn
- eventually leading to termination.
-
- Dual restrictions are put on let corec definitions. If f is a let-rec defined
- term, f is only expanded when it occurs in the term "match f t1 ... tn with ...".
- To better see the duality, that is not syntactically perfect, note that: in
- the recursive case f destructs terms and is expanded only when applied to a
- constructor; in the co-recursive case f constructs terms and is expanded only
- when it becomes argument of the match destructor. Termination is guaranteed
- by the combination of this restriction and f being productive: the term
- "match f t1 ... tn ... with" will reduce in a finite amount of time to
- a match applied to a constructor, whose reduction can expose another application
- of f, but not another "match f t1' .. tn' ... width". Therefore, since no
- new matches around f can be created by reduction, the number of
- destructors that surrounds the application of f decreases at every step,
- eventually leading to termination.
- Even if a coinductively defined f does not reduce in every context to its
- definiendum, it is possible to prove that the definiens is equal to its
- definiendum. The trick is to prove first an eta-expansion lemma for the
- inductive type that states that an inhabitant of the inductive type is
- equal to the one obtained destructing and rebuilding it via a match. The proof
- is simply by cases over the inhabitant. Let's see an example. *)
-
-lemma eta_streamseq:
- ∀A:Type[0]. ∀s: streamseq A.
-  match s with [ sscons hd tl ⇒ sscons … hd tl ] = s.
- #A * //
+(* The previous definition of proper only deals with unary functions. In type
+ theory n-ary functions are better handled in Curryfied form as unary functions
+ whose output is a function space type. When we restrict to morphisms, we do not
+ need a notion of properness for n-ary functions because the function space can
+ also be seen as a setoid quotienting functions using functional extensionality:
+ two morphisms are equal when they map equal elements to equal elements. *)
+definition function_space: setoid → setoid → setoid ≝
+ λI,O.
+  mk_setoid (I ⤞ O)
+   (mk_equivalence_relation … (λf,g. ∀x,y:I. x ≃ y → f x ≃ g y) …).
+ whd
+ [ #f1 #f2 #f3 #f1_f2 #f2_f3 #x #y #EQ @(equiv_trans … (f2 x)) /2/
+ | #f1 #f2 #f1_f2 #x #y #EQ @(equiv_sym … (f1_f2 …)) @equiv_sym //
+ | #f #x #y #EQ @mor_proper // ]
 qed.
 
-(* In order to prove now that the definiens of all_zeros is equal to its
- definiendum, it suffices to rewrite it using the eta_streamseq lemma in order
- to insert around the definiens the match destructor that triggers its
- expansion. *)
-lemma all_zeros_expansion: all_zeros = sscons … 0 all_zeros.
- <(eta_streamseq ? all_zeros) in ⊢ (??%?); //
+unification hint 0 ≔ I,O: setoid;
+    X ≟ function_space I O
+(* ---------------------------------------- *) ⊢
+    I ⤞ O ≡ carrier X.
+
+(* We overload the notation so that I ⤞ O can mean both the type of morphisms
+ from I to O or the function space from I to O. *)
+interpretation "function_space" 'morphism I O = (function_space I O).
+
+(* A binary morphism is just obtained by Currification. In the following
+ we will use I1 ⤞ I2 ⤞ O directly in place of bin_morphism. *)
+definition bin_morphism: setoid → setoid → setoid → Type[0] ≝
+ λI1,I2,O. I1 ⤞ I2 ⤞ O.
+
+(* Directly inhabiting a binary morphism is annoying because one needs to
+ write a function that returns a morphism in place of a binary function.
+ Moreover, there are two proof obligations to prove. We can simplify the work
+ by introducing a new constructor for binary morphisms that takes in input a
+ binary function and opens a single proof obligation, called proper2. *)
+definition proper2: ∀I1,I2,O: setoid. (I1 → I2 → O) → Prop ≝
+ λI1,I2,O,f.
+  ∀x1,x2,y1,y2. x1 ≃ x2 → y1 ≃ y2 → f x1 y1 ≃ f x2 y2.
+  
+definition mk_bin_morphism:
+ ∀I1,I2,O: setoid. ∀f: I1 → I2 → O. proper2 … f → I1 ⤞ I2 ⤞ O ≝
+λI1,I2,O,f,proper.
+  mk_morphism … (λx. mk_morphism … (λy. f x y) …) ….
+ normalize /2/
 qed.
 
-(* Expansions lemmas as the one just presented are almost always required to
- progress in non trivial proofs, as we will see in the next example. Instead
- the equivalent expansions lemmas for let-rec definitions are rarely required.
-*)
+(* We can also coerce a binary morphism to a binary function and prove that
+ proper2 holds for every binary morphism. *)
+definition binary_function_of_binary_morphism:
+ ∀I1,I2,O: setoid. (I1 ⤞ I2 ⤞ O) → (I1 → I2 → O) ≝
+λI1,I2,O,f,x,y. f x y.
 
-(* Example 3 revisited: traces of a program. *)
+coercion binary_function_of_binary_morphism:
+ ∀I1,I2,O: setoid. ∀f:I1 ⤞ I2 ⤞ O. (I1 → I2 → O) ≝
+ binary_function_of_binary_morphism
+ on _f: ? ⤞ ? ⤞ ? to ? → ? → ?.
 
-(* A diverging trace is a streamseq of states such that the n+1-th state is
- obtained executing one step from the n-th state. The definition of
- div_well_formed' is the same we already used for sequences, but on
- streamseqs. *)
+theorem mor_proper2: ∀I1,I2,O: setoid. ∀f: I1 ⤞ I2 ⤞ O. proper2 … f.
+ #I2 #I2 #O #f #x1 #x2 #y1 #y2 #EQx #EQy @(mor_proper … f … EQx … EQy)
+qed. 
 
-definition div_well_formed' : streamseq state → Prop ≝
- λs: streamseq state.
-  ∀n. next (streamseq_nth … s n) (streamseq_nth … s (S n)). 
+(* Example: addition of integer numbers. We define addition as a function
+ before lifting it to a morphism and declaring the hints to automatically
+ prove it proper when needed. *)
+definition Zplus: Z → Z → Z ≝ λx,y. 〈\fst x + \fst y,\snd x + \snd y〉.
 
-record div_trace': Type[0] ≝
- { div_tr':> streamseq state
- ; div_well_formed'': div_well_formed' div_tr' 
- }.
+(* We overload + over integers. *)
+interpretation "Zplus" 'plus x y = (Zplus x y).
 
-(* The well formedness predicate over streamseq can also be redefined using as a
- coinductive predicate. A streamseq of states is well formed if the second
- element is the next of the first and the stream without the first element
- is recursively well formed. *)
-coinductive div_well_formed_co: streamseq state → Prop ≝
- is_next:
-  ∀hd1:state. ∀hd2:state. ∀tl:streamseq state.
-   next hd1 hd2 → div_well_formed_co (sscons … hd2 tl) →
-    div_well_formed_co (sscons … hd1 (sscons … hd2 tl)).
-
-(* Note that Matita automatically proves the inversion principles for every
- coinductive type, but no general coinduction principle. That means that
- the elim tactic does not work when applied to a coinductive type. Inversion
- and cases are the only ways to eliminate a coinductive hypothesis. *)
-
-(* A proof of div_well_formed cannot be built stacking a finite
- number of constructors. The type can only be inhabited by a coinductive
- definition. As an example, we show the equivalence between the two
- definitions of well formedness for streamseqs. *)
-(* A div_well_formed' stream is also div_well_formed_co. We write the proof
- term explicitly, omitting the subterms that prove "next hd1 hd2" and
- "div_well_formed' (sscond … hd2 tl)". Therefore we will obtain two proof
- obligations. The given proof term is productive: if we apply it to a closed
- term of type streamseq state and a proof that it is well formed, the two
- matches in head position will reduce and the lambda-abstraction will be
- consumed, exposing the is_next constructor. *)
-let corec div_well_formed_to_div_well_formed_co
- (s: streamseq state): div_well_formed' s → div_well_formed_co s ≝
- match s with [ sscons hd1 tl1 ⇒
-  match tl1 with [ sscons hd2 tl ⇒
-   λH: div_well_formed' (sscons … hd1 (sscons … hd2 tl)).
-    is_next … (div_well_formed_to_div_well_formed_co (sscons … hd2 tl) …) ]].
-[ (* First proof obligation: next hd1 hd2 *) @(H 0)
-| (* Second proof obligation: div_well_formed' (sscons … hd2 tl) *) @(λn. H (S n)) ]
-qed.  
-
-(* A div_well_formed_co stream is also div_well_formed'. This time the proof is
- by induction over the index and inversion over the div_well_formed_co
- hypothesis. *)
-theorem div_well_formed_co_to_div_well_formed:
- ∀s: streamseq state. div_well_formed_co s → div_well_formed' s.
- #s #H #n lapply H -H lapply s -s elim n [2: #m #IH]
- * #hd1 * #hd2 #tl normalize #H inversion H #hd1' #hd2' #tl' #Hnext #Hwf
- #eq destruct /2/
+definition ZPLUS: Z ⤞ Z ⤞ Z ≝ mk_bin_morphism … Zplus ….
+ normalize * #x1a #x1b * //
 qed.
 
-(* Like for sequences, because of undecidability of termination there is no
- function that given an initial state returns the diverging trace if the program
- diverges or fails in case of error. We need a new data type to represent a
- possibly infinite, possibly terminated stream of elements. Such data type is
- usually called stream and can be defined elegantly as a coinductive type. *)
-coinductive stream (A: Type[0]) : Type[0] ≝
-   snil: stream A
- | scons: A → stream A → stream A.
-
-(* The definition of trace based on streams is more natural than that based
- on sequences of optional states because there is no need of the invariant that
- a None state is followed only by None states (to represent a terminated
- sequence). Indeed, this is the first example where working with coinductive
- types seems to yield advantages in terms of simplicity of the formalization.
- However, in order to give the definition we first need to coinductively define
- the well_formedness predicate, whose definition is more complex than the
- previous one. *)
-coinductive well_formed': stream state → Prop ≝
-   wf_snil: ∀s. step s = None … → well_formed' (scons … s (snil …))
- | wf_scons:
-    ∀hd1,hd2,tl.
-     step hd1 = Some … hd2 →
-      well_formed' (scons … hd2 tl) →
-       well_formed' (scons … hd1 (scons … hd2 tl)).
-
-(* Note: we could have equivalently defined well_formed' avoiding coinduction
- by defining a recursive function to retrieve the n-th element of the stream,
- if any. From now on we will stick to coinductive predicates only to show more
- examples of usage of coinduction. In a formalization, however, it is always
- better to explore several alternatives and see which ones work best for the
- problem at hand. *)
-
-record trace': Type[0] ≝
- { tr':> stream state
- ; well_formed'': well_formed' tr'
- }.
+unification hint 0 ≔ x,y:Z ;
+    X ≟ ZPLUS
+(* ---------------------------------------- *) ⊢
+    x + y ≡ mor_carr … X x y.
 
-(* The trace is diverging if every state is not final. Again, we show here
- a coinductive definition. *)
-coinductive diverging': stream state → Prop ≝
- mk_diverging': ∀hd,tl. diverging' tl → diverging' (scons … hd tl).
-
-(* The two coinductive definitions of diverging traces are equivalent.
- To state the two results we first need a function to retrieve the head
- from traces and diverging traces. *)
-definition head_of_streamseq: ∀A:Type[0]. streamseq A → A ≝
- λA,s. match s with [ sscons hd _ ⇒ hd ].
-
-definition head_of_stream: ∀A:Type[0]. stream A → option A ≝
- λA,s. match s with [ snil ⇒ None … | scons hd _ ⇒ Some … hd ].
-
-(* A streamseq can be extracted from a diverging stream using corecursion. *)
-let corec mk_diverging_trace_to_div_trace'
- (s: stream state) : diverging' s → streamseq state ≝
- match s return λs. diverging' s → streamseq state
- with
- [ snil ⇒ λabs: diverging' (snil …). ?
- | scons hd tl ⇒ λH. sscons ? hd (mk_diverging_trace_to_div_trace' tl …) ].
- [ cases (?:False) inversion abs #hd #tl #_ #abs' destruct
- | inversion H #hd' #tl' #K #eq destruct @K ]
+(* The identity function is a morphism and composition of morphisms is also
+ a morphism. This means that the identity function is always proper and
+ a compound context is proper if every constituent is. *)
+definition id_morphism: ∀S: setoid. S ⤞ S ≝
+ λS. mk_morphism … (λx.x) ….
+ //
 qed.
 
-(* An expansion lemma will be needed soon. *)
-lemma mk_diverging_trace_to_div_trace_expansion:
- ∀hd,tl,H. ∃K.
-  mk_diverging_trace_to_div_trace' (scons state hd tl) H =
-   sscons … hd (mk_diverging_trace_to_div_trace' tl K).
- #hd #tl #H cases (eta_streamseq … (mk_diverging_trace_to_div_trace' ??)) /2/
+definition comp_morphism: ∀S1,S2,S3. (S2 ⤞ S3) → (S1 ⤞ S2) → (S1 ⤞ S3) ≝
+ λS1,S2,S3,f1,f2. mk_morphism … (λx. f1 (f2 x)) ….
+ normalize #x1 #x2 #EQ @mor_proper @mor_proper //
 qed.
 
-(* CSC: BUG CHE APPARE NEL PROSSIMO LEMMA AL MOMENTO DELLA QED. IL DEMONE
- SERVE PER DEBUGGARE *)
-axiom daemon: False.
-
-(* To complete the proof we need a final lemma: streamseqs extracted from
- well formed diverging streams are well formed too. *) 
-let corec div_well_formed_co_mk_diverging_trace_to_div_trace (t : stream state) :
- ∀H:diverging' t. div_well_formed_co (mk_diverging_trace_to_div_trace' t H) ≝
- match t return λt. diverging' t → ?
- with
- [ snil ⇒ λabs. ?
- | scons hd tl ⇒ λH. ? ].
-[ cases (?:False) inversion abs #hd #tl #_ #eq destruct
-| cases (mk_diverging_trace_to_div_trace_expansion … H) #H' #eq
-  lapply (sym_eq ??? … eq) #Req cases Req -Req -eq -H
-  cases tl in H';
-  [ #abs cases (?:False) inversion abs #hd #tl #_ #eq destruct
-  | -tl #hd2 #tl #H
-    cases (mk_diverging_trace_to_div_trace' … H) #H'
-    #eq lapply (sym_eq ??? … eq) #Req cases Req -Req
-    % [2: (*CSC: BIG BUG HERE*) cases daemon (* cases eq @div_well_formed_co_mk_diverging_trace_to_div_trace *)
-      | cases daemon ]]]
+(*
+(* The following hint is an example of proof automation rule. It says that
+ f1 (f2 x) can be seen to be the application of the morphism
+ comp_morphism F1 F2 to x as long as two morphisms F1 and F2 can be
+ associated to f1 and f2. *)
+unification hint 0 ≔
+ S1,S2,S3: setoid, f1: S2 → S3, f2: S1 → S2, x:S1, F1: S2 ⤞ S3, F2: S1 ⤞ S2;
+    f1 ≟ mor_carr … F1,
+    f2 ≟ mor_carr … F2,
+    X ≟ comp_morphism … F1 F2
+(* ---------------------------------------- *) ⊢
+    f1 (f2 x) ≡ mor_carr … X x. *)
+
+(* By iterating applications of mor_proper, we can consume the context bit by
+ bit in order to perform a rewriting. Like in ex2, the script works on any
+ setoid because it does not reference OPP anywhere. The above theorem on
+ composition of morphisms justify the correctness of the scripts. *)
+example ex3: ∀x,y. x ≃ y → opp (opp (opp x)) ≃ opp (opp (opp y)).
+ #x #y #EQ @mor_proper @mor_proper @mor_proper @EQ
 qed.
 
-theorem diverging_trace_to_div_trace':
- ∀t: trace'. diverging' t → ∃d: div_trace'.
-  head_of_stream … t = Some … (head_of_streamseq … d).
- #t #H %
- [ %{(mk_diverging_trace_to_div_trace' … H)}
- | cases t in H; * normalize // #abs cases (?:False) inversion abs
-   [ #s #_ #eq destruct | #hd1 #hd2 #tl #_ #_ #eq destruct ]]
-  #n  lapply (well_formed t n)
-   lapply (H n) cases (tr t n) normalize [ * #abs cases (abs …) // ]
-   * #o #k #_ lapply (H (S n)) -H
-   cases (tr t (S n)) normalize
-   [ * #abs cases (abs …) // ] * #o' #k' #_ #EQ lapply (EQ … (refl …)) -EQ
-     normalize cases k normalize [ #abs destruct ] #hd #tl #EQ destruct -EQ
-     @step_next >e0 // ]
- | lapply (H O) -H cases (tr t O) [ * #abs cases (abs …) // ] // ]
-qed.
+(* We can improve the readability of the previous script by assigning
+ a notation to mor_proper and by packing together the various applications
+ of mor_proper and EQ. We pick the prefix symbol †. *)
+notation "† c" with precedence 90 for @{'proper $c }.
 
-(* A stream can be extracted from a streamseq using corecursion. *)
-let corec stream_of_streamseq (A: Type[0]) (s: streamseq A) : stream A ≝
- match s with [ sscons hd tl ⇒ scons … hd (stream_of_streamseq … tl) ].
+interpretation "mor_proper" 'proper c  = (mor_proper ????? c).
 
-(* The proof that the resulting stream is diverging also need corecursion.*)
-let corec diverging_stream_of_streamseq (s: streamseq state) :
- diverging' (stream_of_streamseq … s) ≝
- match s return λs. diverging' (stream_of_streamseq … s)
- with [ sscons hd tl ⇒ mk_diverging' … ].
-  mk_diverging' hd (stream_of_streamseq … tl) (diverging_stream_of_streamseq tl) ].
+example ex3': ∀x,y. x ≃ y → opp (opp (opp x)) ≃ opp (opp (opp y)).
+ #x #y #EQ @(†(†(†EQ)))
+qed.
+
+(* While the term (†(†(†EQ))) can seem puzzling at first, note that it
+ closely matches the term (opp (opp (opp x))). Each occurrence of the
+ unary morphism opp is replaced by † and the occurrence x to be rewritten to y
+ is replaced by EQ: x ≃ y. Therefore the term (†(†(†EQ))) is a compact
+ notation to express at once where the rewriting should be performed and
+ what hypothesis should be used for the rewriting. We will explain now
+ the problem of rewriting setoid equalities in full generality, and a lightweight
+ solution to it. *)
+
+(****** Rewriting setoid equalities *********)
+
+(* In set theory, once a quotient S/R has been defined, its elements are
+ compared using the standard equality = whose main property is that of replacing
+ equals to equals in every context. If E1=E2 then f E1 can be replaced with f E2
+ for every context f. Note that f is applied to equivalence classes of elements
+ of S. Therefore every function and property in f must have been lifted to work
+ on equivalence classes, and this was possible only if f respected R.
  
+ When using setoids we keep working with elements of S instead of forming a new
+ type. Therefore, we must deal with contexts f that are not proper. When f is
+ not proper, f E1 cannot be replaced with f E2 even if E1 ≃ E2. For example,
+ 〈0,1〉 ≃ 〈1,2〉 but \fst 〈0,1〉 ≠ \fst 〈1,2〉. Therefore every time we want to
+ rewrite E1 with E2 under the assumption that E1 ≃ E2 we need to prove the
+ context to be proper. Most of the time the context is just a composition of
+ morphisms and, like in ex3', the only information that the user needs to
+ give to the system is the position of the occurrences of E1 to be replaced
+ and the equations to be used for the rewriting. As for ex3', we can provide
+ a simple syntax to describe contexts and equations at the same time. The
+ syntax is just given by a few notations to hide applications of mor_proper,
+ reflexivity, symmetry and transitivity.
+
+ Here is a synopsis of the syntax:
+ -  †_   to rewrite in the argument of a unary morphism
+ - _‡_   to rewrite in both arguments of a binary morphism
+ -  #    to avoid rewriting in this position
+ -  t    to rewrite from left to right in this position using the proof t.
+         Usually t is the name of an hypothesis in the context of type E1 ≃ E2
+ - t^-1  to rewrite from right to left in this position using the proof t.
+         Concretely, it applies symmetry to t, proving E2 ≃ E1 from E1 ≃ E2.
+ - ._    to start rewriting when the goal is not an equation ≃.
+         Concretely, it applies the proof of P E2 → P E1 obtained by splitting
+         the double implication P E2 ↔ P E1, which is equivalent to P E2 ≃ P E1
+         where ≃ is the equality of the PROP setoid. Thus the argument should
+         be a proof of P E2 ≃ P E1, obtained using the previous symbols according
+         to the shape of P.
+ - .=_   to prove an equation G1 ≃ G2 by first rewriting into E1 leaving a new
+         goal G1' ≃ G2. Concretely, it applies transitivity of ≃.
+*)
 
-theorem div_trace_to_diverging_trace':
- ∀d: div_trace'. ∃t: trace'. diverging' t ∧
-  head_of_stream … t = Some … (head_of_streamseq … d).
- #d %{(mk_trace' (stream_of_streamseq … d) …)}
- [2: %
-   [  
-   [2: cases d * // ] #n % #abs destruct
- | #n #s #EQ destruct lapply (div_well_formed d n) /2 by div_well_formed, next_step/ ]
-qed.
+notation "l ‡ r" with precedence 90 for @{'proper2 $l $r }.
+
+interpretation "mor_proper2" 'proper2 x y = (mor_proper ? (function_space ? ?) ?? ? x ?? y).
+
+notation "#" with precedence 90 for @{'reflex}.
+
+interpretation "reflexivity" 'reflex = (equiv_refl ???).
+
+interpretation "symmetry" 'invert r = (equiv_sym ???? r).
+
+notation ".= r" with precedence 55 for @{'trans $r}.
 
-(* AGGIUNGERE SPIEGAZIONE SU PRODUTTIVITA' *)
+interpretation "transitivity" 'trans r = (equiv_trans ????? r ?).
 
-(* AGGIUNGERE SPIEGAZIONE SU CONFRONTO VALORI COINDUTTIVI *)
+notation ". r" with precedence 55 for @{'fi $r}.
 
-(* AGGIUNGERE CONFRONTO CON TEORIA DELLE CATEGORIE *)
+definition fi: ∀A,B:Prop. A ≃ B → (B → A) ≝ λA,B,r. proj2 ?? r.
 
-(* AGGIUNGERE ESEMPI DI SEMANTICA OPERAZIONE BIG STEP PER LA DIVERGENZA;
-   DI PROPRIETA' DI SAFETY;
-   DI TOPOLOGIE COINDUTTIVAMENTE GENERATE? *)
+interpretation "fi" 'fi r = (fi ?? r).
 
-(* ################## COME SPIEGARLO QUI? ####################### *)
+(* The following example shows several of the features at once:
+   1. the first occurrence of x2 is turned into x1 by rewriting the hypothesis
+      from right to left.
+   2. the first occurrence of x1 is turned into x2 by rewriting the hypothesis
+      from left to right.
+   3. the two rewritings are performed at once.
+   4. the subterm z+y does not need to be rewritten. Therefore a single # is
+      used in place of #‡#, which is also correct but produces a larger proof.
+   5. we can directly start with an application of ‡ because the goal is a
+      setoid equality *)
+example ex4: ∀x1,x2,y,z:Z. x1 ≃ x2 →
+ (y + x2) + (x1 + (z + y)) ≃ (y + x1) + (x2 + (z + y)).
+ #x1 #x2 #y #z #EQ @((#‡EQ^-1)‡(EQ‡#))
+qed.
 
+(* The following example is just to illustrate the use of .=. We prove the
+ same statement of ex4, but this time we perform one rewriting at a time.
+ Note that in an intermediate goal Matita replaces occurrences of Zplus with
+ occurrences of (the carrier of) ZPLUS. To recover the notation + it is
+ sufficient to expand the declaration of ZPLUS. *)
+example ex5: ∀x1,x2,y,z:Z. x1 ≃ x2 →
+ (y + x2) + (x1 + (z + y)) ≃ (y + x1) + (x2 + (z + y)).
+ #x1 #x2 #y #z #EQ @(.=(#‡EQ^-1)‡#) whd in match ZPLUS; @(#‡(EQ‡#))
+qed.
 
-(*let corec stream_coind (A: Type[0]) (P: Prop) (H: P → Sum unit (A × P))
- (p:P) : stream A ≝
- match H p with
- [ inl _ ⇒ snil A
- | inr cpl ⇒ let 〈hd,p'〉 ≝ cpl in scons A hd (stream_coind A P H p') ]. *)
+(* Our last example involves rewriting under a predicate different from ≃.
+ We first introduce such a predicate over integers. *)
+definition is_zero: Z → Prop ≝ λc. \fst c = \snd c.
 
-(*lemma eta_streamseq:
- ∀A:Type[0]. ∀s: streamseq A.
-  s = match s with [ sscons hd tl ⇒ sscons … hd tl ].
- #A * //
+definition IS_ZERO: Z ⤞ PROP ≝ mk_morphism … is_zero ….
+ normalize /3 by conj,injective_plus_r/
 qed.
 
-lemma Rplus_streamseq_nf:
- ∀xhd,xtl,yhd,ytl.
-  Rplus_streamseq (sscons … xhd xtl) (sscons … yhd ytl) =
-   sscons … (xhd + yhd) (Rplus_streamseq xtl ytl).
- #xhd #xtl #yhd #ytl >(eta_streamseq Q (Rplus_streamseq …)) in ⊢ (??%?); //
-qed.*)
+unification hint 0 ≔ x:Z ;
+    X ≟ IS_ZERO
+(* ---------------------------------------- *) ⊢
+    is_zero x ≡ mor_carr … X x.
+
+(* We can rewrite under any predicate starting with . *)
+example ex6: ∀x,y:Z. x ≃ y → is_zero (x + y) → is_zero (x + x).
+ #x #y #EQ #H @(.†(#‡EQ)) @H
+qed.
 
+(****** Dependent setoids ********)
+
+(* A setoid is essentially a type equipped with its own notion of equality.
+ In a dependent type theory, one expects to be able to both build types (and
+ setoids) dependent on other types (and setoids). Working with families of
+ setoids that depend over a plain type --- i.e. not another setoid --- pauses
+ no additional problem. For example, we can build a setoid out of vectors of
+ length n assigning to it the type ℕ → setoid. All the machinery for setoids
+ just introduced keeps working. On the other hand, types that depend over a
+ setoid require a much more complex machinery and, in practice, it is not
+ advised to try to work with them in an intentional type theory like the one
+ of Matita.
+ To understand the issue, imagine that we have defined a family of
+ types I dependent over integers: I: Z → Type. Because 〈0,1〉 and 〈1,2〉 both
+ represent the same integer +1, the two types I 〈0,1〉 and of I 〈1,2〉 should have
+ exactly the same inhabitants. However, being different types, their inhabitants
+ are disjoint. The solution is to equip the type I with a transport function
+ t: ∀n,m:Z. n ≃ m → I n → I m  that maps an element of I n to the corresponding
+ element of I m. Starting from this idea, the picture quickly becomes complex
+ when one start considering all the additional equations that the transport
+ functions should satisfy. For example, if p: n ≃ m, then t … p (t … p^-1 x) = x,
+ i.e. the element in I n corresponding to the element in I m that corresponds to
+ x in I n should be exactly x. Moreover, for any function f: ∀n. I n → T n for
+ some other type T dependent over n the following equation should hold:
+ f … (t … p x) = t … p (f … x) (i.e. transporting and applying f should commute
+ because f should be insensitive too up to ≃ to the actual representation of the
+ integral indexes).
+
+ Luckily enough, in practice types dependent overs setoids occur very rarely.
+ Most examples of dependent types are indexed over discrete objects, like
+ natural, integers and rationals, that admit an unique representation.
+ For continuity reasons, types dependent over real numbers can also be
+ represented as types dependent over a dense subset of the reals, like the
+ rational numbers. *)
+
+(****** Avoiding setoids *******)
+
+(* Quotients are used pervasively in mathematics. In many practical situations,
+ for example when dealing with finite objects like pairs of naturals, an unique
+ representation can be imposed, for example by introducing a normalization
+ procedure to be called after every operation. For example, integer numbers can
+ be normalized to either 〈0,n〉 or 〈n,0〉. Or they can be represented as either 0
+ or a non zero number, with the latter being encoded by a boolean (the sign) and
+ a natural (the predecessor of the absolute value). For example, -3 would be
+ represented by NonZero 〈false,2〉, +3 by NonZero 〈true,2〉 and 0 by Zero.
+ Rational numbers n/m can be put in normal form by dividing both n and m by their
+ greatest common divisor, or by picking n=0, m=1 when n is 0. These normal form
+ is an unique representation.
+ Imposing an unique representation is not always possible. For example, picking
+ a canonical representative for a Cauchy sequence is not a computable operation.
+ Nevertheless, when possible, avoiding setoids is preferrable:
+ 1) when the Leibniz equality is used, replacing n with m knowing n=m does not
+    require any proof of properness
+ 2) at the moment automation in Matita is only available for Leibniz equalities.
+    By switching to setoids less proofs are automatically found
+ 3) types dependent over plain types do not need ad-hoc transport functions
+    because the rewriting principle for Leibniz equality plays that role and
+    already satisfies for free all required equations
+ 4) normal forms are usually smaller than other forms. By sticking to normal
+    forms both the memory consumption and the computational cost of operations
+    may be reduced. *)