seance3-english.md 5.19 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
Functional Programming in Coq (session 3)
=========================================

**M2 LMFI**

## Advanced Inductive Types

#### Ordinals

We can encode in Coq (some) ordinals, via the following type :

```coq
Inductive ord :=
 | zero : ord
 | succ : ord -> ord
 | lim : (nat->ord) -> ord.
```

Note that this inductive type does satisfy the strict positivity constraint: constructor `lim` has an argument of type `nat->ord`, where `ord` appears indeed on the right. Having instead `lim:(ord->nat)->ord` would be refused by Coq.

We can plunge in this type the usual natural numbers of type `nat`.
For instance via a mixte addition `add : ord -> nat -> ord` :

```coq
Fixpoint add a n :=
 match n with
 | 0 => a
 | S n => succ (add a n)
end.
Definition nat2ord n := add zero n.
```

Now, we could use constructor `lim` and this `add` function to go beyond the usual numbers.

```coq
Definition omega := lim (add zero).
Definition deuxomega := lim (add omega).

Fixpoint nomega n :=
 match n with
 | 0 => zero
 | S n => lim (add (nomega n))
 end.
 
Definition omegadeux := lim nomega.
```

Be careful, the standard equality of Coq is not very meaningful on these ordinals, since it is purely syntactic. For instance `add zero` and `add (succ zero)` are two different sequences (numbers starting at 0 vs. numbers starting at 1). So Coq will allow proving that `lim (add zero) <> lim (add (succ zero))` (where `<>` is the negation of the logical equality `=`). But we usually consider the limits of these two sequences to be two possible descriptions of `omega`, the first infinite ordinal. We would then have to define and use a specific equality on `ord`, actually an equivalence relation (we also call that a *setoid equality*).

#### Trees of variable arity

Let's encode a type of trees made of nodes having a natural number on them, and then an arbitrary number of subtrees, not just two like last week's `tree`.

```coq
Inductive ntree :=
 | Node : nat -> list ntree -> ntree.
```

Note that this inductive type need not have a "base" constructeur like `O` for `nat` or `leaf` for last week `tree`. Instead, we could use `Node n []` for representing a leaf.

An example of program over this type:

```coq
Require Import List.
Import ListNotations.

(* Addition of all elements of a list of natural numbers *)
Fixpoint sum (l:list nat) : nat :=
 match l with
 | [] => 0
 | x::l => x + sum l
 end.

(* List.map : iterating a function over all elements of a list *)
Check List.map.

(* How many nodes in a ntree ? *)
Fixpoint ntree_size t :=
 match t with
 | Node _ ts => 1 + sum (List.map ntree_size ts)
 end.
```

Why is this function `ntree_size` accepted as strictly decreasing ? Indee `ts` is a subpart of `t`, but we are not launching the recursive call on `ts` itself. Fortunately, here Coq is clever enough to enter the code of `List.map` and see that `ntree_size` will be launched on subparts of `ts`, and hence transitively subparts of `t`. But that trick only works for a specific implementation of `List.map` (check with your own during the practical session).



## Internal recursive function : fix

Is the Ackermann function structurally decreasing ?

 - `ack 0 m = m+1`
 - `ack (n+1) 0 = ack n 1`
 - `ack (n+1) (m+1) = ack n (ack (n+1) m)`

No if we consider only one argument, as Coq does. Indeed, neither `n` nor `m` (taken separately) ensures a strict decrease. But there is a trick (quite standard now) : we could separate this function into an external fixpoint (decreasing on `n`) and an internal fixpoint (decreasing on `m`), and hence emulate a lexicographic ordering on the arguments. The inner fixpoint uses the `fix` syntax :

```coq
Fixpoint ack n :=
 match n with
 | 0 => S
 | S n =>
   fix ack_Sn m :=
   match m with
   | 0 => ack n 1
   | S m => ack n (ack_Sn m)
   end
 end.

Compute ack 3 5.
```

## Induction Principles

For each new inductive type declared by the user, Coq automatically generates particular functions named induction principles. Normally, for a type `foo`, we get in particular a function `foo_rect`. This function mimics the shape of the inductive type for providing an induction dedicated to this type. For instead for `nat` :

```coq
Check nat_rect.
Print nat_rect.
```

Deep inside this `nat_rect`, one finds a `fix` and a `match`, and this recursion and case analysis is just as generic as it could be for `nat` :
we could program on `nat` without any more `Fixpoint` nor `fix` nor `match`, just with `nat_rect`! For instance:

```coq
Definition pred n : nat := nat_rect _ 0 (fun n h => n) n.
Definition add n m : nat := nat_rect _ m (fun _ h => S h) n.
```

In these two cases, the "predicate" `P` needed by `nat_rect` (its first argument `_`) is actually `fun _ => nat`, meaning that we are using `nat_rect` in a non-dependent manner (more on that in a forthcoming session).

## Pseudo Induction Principles

Example of `Pos.peano_rect` and `N.peano_rect` (mentioned in the solution of TD2) : we could manually "hijack" the (binary) recursion on type `positive` for building a peano-like induction principle following (apparently) a unary recursion. Check in particular that `Pos.peano_rect` is indeed structurally decreasing.

```coq
Require Import PArith NArith.
Check Pos.peano_rect.
Check N.peano_rect.
Print Pos.peano_rect.
Print N.peano_rect.
```