Merge branch 'mailmap' into 'master'
[why3.git] / examples / defunctionalization.mlw
blob23f0d439fcbe3b6f6664834856cffa1430a2ae0e
1 (**
3 {1 Defunctionalization}
5 This is inspired from student exercises proposed by
6 {h <a href="http://cs.au.dk/~danvy/">Olivier Danvy</a>}
7 at the {h <a href="http://jfla.inria.fr/2014/">JFLA 2014 conference</a>}
9 *)
12 (** {2 Simple Arithmetic Expressions} *)
14 module Expr
16 use export int.Int
18 (** Grammar of expressions
19 {h <blockquote><pre>
20 n  :  int
22 e  :  expression
23 e ::= n | e - e
25 p  :  program
26 p ::= e
27 </pre></blockquote>}
30 type expr = Cte int | Sub expr expr
32 type prog = expr
34 (** Examples:
35 {h <blockquote><pre>
36 p0 = 0
37 p1 = 10 - 6
38 p2 = (10 - 6) - (7 - 2)
39 p3 = (7 - 2) - (10 - 6)
40 p4 = 10 - (2 - 3)
41 </pre></blockquote>}
44 let constant p0 : prog = Cte 0
46 let constant p1 : prog = Sub (Cte 10) (Cte 6)
48 let constant p2 : prog = Sub (Sub (Cte 10) (Cte 6)) (Sub (Cte 7) (Cte 2))
50 let constant p3 : prog = Sub (Sub (Cte 7) (Cte 2)) (Sub (Cte 10) (Cte 6))
52 let constant p4 : prog = Sub (Cte 10) (Sub (Cte 2) (Cte 3))
54 end
57 (** {2 Direct Semantics} *)
59 module DirectSem
61 use Expr
63 (** Values:
64 {h <blockquote><pre>
65 v  :  value
66 v ::= n
67 </pre></blockquote>
69 Expressible Values:
70 {h <blockquote><pre>
71 ve  :  expressible_value
72 ve ::= v
73 </pre></blockquote>}
74 Interpretation:
75 {h <blockquote><pre>
76 ------
77 n => n
79 e1 => n1   e2 => n2   n1 - n2 = n3
80 ----------------------------------
81       e1 - e2 => n3
82 </pre></blockquote>}
83 A program e is interpreted into a value n if judgement
84 {h <blockquote><pre>
85   e => n
86 </pre></blockquote>}
87 holds.
90 (** {4 Exercise 0.0}
91   Program the interpreter above and test it on the examples.
92 {h <blockquote><pre>
93   eval_0 : expression -> expressible_value
94   interpret_0 : program -> expressible_value
95 </pre></blockquote>}
98 (* Note: Why3 definitions introduced by "function" belong to the logic
99    part of Why3 language *)
101 let rec function eval_0 (e:expr) : int =
102   match e with
103   | Cte n -> n
104   | Sub e1 e2 -> eval_0 e1 - eval_0 e2
105   end
107 let function interpret_0 (p:prog) : int = eval_0 p
109 (** Tests, can be replayed using
110 {h <blockquote><pre>
111   why3 defunctionalization.mlw --exec DirectSem.test
112 </pre></blockquote>}
113 (Why3 version at least 0.82 required)
117 let test () =
118    interpret_0 p0,
119    interpret_0 p1,
120    interpret_0 p2,
121    interpret_0 p3,
122    interpret_0 p4
125 constant v3 : int = eval_0 p3
127 goal eval_p3 : v3 = 1
132 (** {2 CPS: Continuation Passing Style} *)
134 module CPS
136 use Expr
138 (** {4 Exercise 0.1}
140   CPS-transform (call by value, left to right) the function `eval_0`,
141   and call it from the main interpreter with the identity function as
142   initial continuation
143 {h <blockquote><pre>
144       eval_1 : expression -> (expressible_value -> 'a) -> 'a
145   interpret_1 : program -> expressible_value
146 </pre></blockquote>}
150 use DirectSem
152 let rec eval_1 (e:expr) (k: int->'a) : 'a
153   variant { e }
154   ensures { result = k (eval_0 e) }
155 = match e with
156     | Cte n -> k n
157     | Sub e1 e2 ->
158       eval_1 e1 (fun v1 -> eval_1 e2 (fun v2 -> k (v1 - v2)))
159   end
161 let interpret_1 (p : prog) : int
162   ensures { result = eval_0 p }
163 = eval_1 p (fun n -> n)
169 (** {2 Defunctionalization} *)
171 module Defunctionalization
173 use Expr
174 use DirectSem
176 (** {4 Exercise 0.2}
178   De-functionalize the continuation of `eval_1`.
179 {h <blockquote><pre>
180          cont ::= ...
182    continue_2 : cont -> value -> value
183        eval_2 : expression -> cont -> value
184   interpret_2 : program -> value
185 </pre></blockquote>}
186   The data type `cont` represents the grammar of contexts.
188   The two mutually recursive functions `eval_2` and `continue_2`
189   implement an abstract machine, that is a state transition system.
193 type cont = A1 expr cont | A2 int cont | I
195 (** One would want to write in Why:
196 {h <blockquote><pre>
197 function eval_cont (c:cont) (v:int) : int =
198   match c with
199   | A1 e2 k ->
200     let v2 = eval_0 e2 in
201     eval_cont (A2 v k) v2
202   | A2 v1 k -> eval_cont k (v1 - v)
203   | I -> v
204   end
205 </pre></blockquote>}
206 But since the recursion is not structural, Why3 kernel rejects it
207 (definitions in the logic part of the language must be total)
209 We replace that with a relational definition, an inductive one.
213 inductive eval_cont cont int int =
214 | a1 :
215   forall e2:expr, k:cont, v:int, r:int.
216   eval_cont (A2 v k) (eval_0 e2) r -> eval_cont (A1 e2 k) v r
217 | a2 :
218   forall v1:int, k:cont, v:int, r:int.
219   eval_cont k (v1 - v) r -> eval_cont (A2 v1 k) v r
220 | a3 :
221   forall v:int. eval_cont I v v
223 (** Some functions to serve as measures for the termination proof *)
225 function size_e (e:expr) : int =
226   match e with
227   | Cte _ -> 1
228   | Sub e1 e2 -> 3 + size_e e1 + size_e e2
229   end
231 lemma size_e_pos: forall e: expr. size_e e >= 1
233 function size_c (c:cont) : int =
234   match c with
235   | I -> 0
236   | A1 e2 k -> 2 + size_e e2 + size_c k
237   | A2 _ k -> 1 + size_c k
238   end
240 lemma size_c_pos: forall c: cont. size_c c >= 0
242 (** WhyML programs (declared with `let` instead of `function`),
243     mutually recursive, resulting from de-functionalization *)
245 let rec continue_2 (c:cont) (v:int) : int
246   variant { size_c c }
247   ensures { eval_cont c v result }
248   =
249   match c with
250     | A1 e2 k -> eval_2 e2 (A2 v k)
251     | A2 v1 k -> continue_2 k (v1 - v)
252     | I -> v
253   end
255 with eval_2 (e:expr) (c:cont) : int
256   variant { size_c c + size_e e }
257   ensures { eval_cont c (eval_0 e) result }
258   =
259   match e with
260     | Cte n -> continue_2 c n
261     | Sub e1 e2 -> eval_2 e1 (A1 e2 c)
262   end
264 (** The interpreter. The post-condition specifies that this program
265     computes the same thing as `eval_0` *)
267 let interpret_2 (p:prog) : int
268   ensures { result = eval_0 p }
269   = eval_2 p I
271 let test () =
272    interpret_2 p0,
273    interpret_2 p1,
274    interpret_2 p2,
275    interpret_2 p3,
276    interpret_2 p4
281 (** {2 Defunctionalization with an algebraic variant} *)
283 module Defunctionalization2
285 use Expr
286 use DirectSem
288 (** {4 Exercise 0.2}
290   De-functionalize the continuation of `eval_1`.
291 {h <blockquote><pre>
292          cont ::= ...
294    continue_2 : cont -> value -> value
295        eval_2 : expression -> cont -> value
296   interpret_2 : program -> value
297 </pre></blockquote>}
298   The data type `cont` represents the grammar of contexts.
300   The two mutually recursive functions `eval_2` and `continue_2`
301   implement an abstract machine, that is a state transition system.
305 type cont = A1 expr cont | A2 int cont | I
307 (** One would want to write in Why:
308 {h <blockquote><pre>
309 function eval_cont (c:cont) (v:int) : int =
310   match c with
311   | A1 e2 k ->
312     let v2 = eval_0 e2 in
313     eval_cont (A2 v k) v2
314   | A2 v1 k -> eval_cont k (v1 - v)
315   | I -> v
316   end
317 </pre></blockquote>}
318 But since the recursion is not structural, Why3 kernel rejects it
319 (definitions in the logic part of the language must be total)
321 We replace that with a relational definition, an inductive one.
325 inductive eval_cont cont int int =
326 | a1 :
327   forall e2:expr, k:cont, v:int, r:int.
328   eval_cont (A2 v k) (eval_0 e2) r -> eval_cont (A1 e2 k) v r
329 | a2 :
330   forall v1:int, k:cont, v:int, r:int.
331   eval_cont k (v1 - v) r -> eval_cont (A2 v1 k) v r
332 | a3 :
333   forall v:int. eval_cont I v v
335 (** Peano naturals to serve as the measure for the termination proof *)
337 type nat = S nat | O
339 function size_e (e:expr) (acc:nat) : nat =
340   match e with
341   | Cte _ -> S acc
342   | Sub e1 e2 -> S (size_e e1 (S (size_e e2 (S acc))))
343   end
345 function size_c (c:cont) (acc:nat) : nat =
346   match c with
347   | I -> acc
348   | A1 e2 k -> S (size_e e2 (S (size_c k acc)))
349   | A2 _ k -> S (size_c k acc)
350   end
352 (** WhyML programs (declared with `let` instead of `function`),
353    mutually recursive, resulting from de-functionalization *)
355 let rec continue_2 (c:cont) (v:int) : int
356   variant { size_c c O }
357   ensures { eval_cont c v result }
358   =
359   match c with
360     | A1 e2 k -> eval_2 e2 (A2 v k)
361     | A2 v1 k -> continue_2 k (v1 - v)
362     | I -> v
363   end
365 with eval_2 (e:expr) (c:cont) : int
366   variant { size_e e (size_c c O) }
367   ensures { eval_cont c (eval_0 e) result }
368   =
369   match e with
370     | Cte n -> continue_2 c n
371     | Sub e1 e2 -> eval_2 e1 (A1 e2 c)
372   end
374 (** The interpreter. The post-condition specifies that this program
375     computes the same thing as `eval_0` *)
377 let interpret_2 (p:prog) : int
378   ensures { result = eval_0 p }
379   = eval_2 p I
381 let test () =
382    interpret_2 p0,
383    interpret_2 p1,
384    interpret_2 p2,
385    interpret_2 p3,
386    interpret_2 p4
391 (** {2 Semantics with errors} *)
393 module SemWithError
395 use Expr
397 (** Errors:
398 {h <blockquote><pre>
399 s  :  error
400 </pre></blockquote>}
401 Expressible values:
402 {h <blockquote><pre>
403 ve  :  expressible_value
404 ve ::= v | s
405 </pre></blockquote>}
408 type value = Vnum int | Underflow
409 (* in (Vnum n), n should always be >= 0 *)
411 (** Interpretation:
412 {h <blockquote><pre>
413 ------
414 n => n
416      e1 => s
417 ------------
418 e1 - e2 => s
420 e1 => n1   e2 => s
421 ------------------
422       e1 - e2 => s
424 e1 => n1   e2 => n2   n1 < n2
425 -----------------------------
426       e1 - e2 => "underflow"
428 e1 => n1   e2 => n2   n1 >= n2   n1 - n2 = n3
429 ---------------------------------------------
430       e1 - e2 => n3
431 </pre></blockquote>}
432 We interpret the program `e` into value `n` if the judgement
433 {h <blockquote><pre>
434   e => n
435 </pre></blockquote>}
436 holds, and into error `s` if the judgement
437 {h <blockquote><pre>
438   e => s
439 </pre></blockquote>}
440 holds.
443 {4 Exercise 1.0}
445   Program the interpreter above and test it on the examples.
446 {h <blockquote><pre>
447   eval_0 : expr -> expressible_value
448   interpret_0 : program -> expressible_value
449 </pre></blockquote>}
452 function eval_0 (e:expr) : value =
453   match e with
454   | Cte n -> if n >= 0 then Vnum n else Underflow
455   | Sub e1 e2 ->
456      match eval_0 e1 with
457      | Underflow -> Underflow
458      | Vnum v1 ->
459        match eval_0 e2 with
460        | Underflow -> Underflow
461        | Vnum v2 ->
462          if v1 >= v2 then Vnum (v1 - v2) else Underflow
463        end
464      end
465   end
467 function interpret_0 (p:prog) : value = eval_0 p
471 (** {4 Exercise 1.1}
472   CPS-transform (call by value, from left to right)
473   the function `eval_0`, call it from the main interpreter
474   with the identity function as initial continuation.
475 {h <blockquote><pre>
476       eval_1 : expr -> (expressible_value -> 'a) -> 'a
477   interpret_1 : program -> expressible_value
478 </pre></blockquote>}
482 function eval_1 (e:expr) (k:value -> 'a) : 'a =
483   match e with
484     | Cte n -> k (if n >= 0 then Vnum n else Underflow)
485     | Sub e1 e2 ->
486       eval_1 e1 (fun v1 ->
487        match v1 with
488        | Underflow -> k Underflow
489        | Vnum v1 ->
490          eval_1 e2 (fun v2 ->
491          match v2 with
492          | Underflow -> k Underflow
493          | Vnum v2 -> k (if v1 >= v2 then Vnum (v1 - v2) else Underflow)
494          end)
495        end)
496   end
498 function interpret_1 (p : prog) : value = eval_1 p (fun n ->  n)
501 lemma cps_correct_expr:
502   forall e: expr. forall k:value -> 'a. eval_1 e k = k (eval_0 e)
504 lemma cps_correct: forall p. interpret_1 p = interpret_0 p
506 (** {4 Exercise 1.2}
508   Divide the continuation
509 {h <blockquote><pre>
510     expressible_value -> 'a
511 </pre></blockquote>}
512   in two:
513 {h <blockquote><pre>
514     (value -> 'a) * (error -> 'a)
515 </pre></blockquote>}
516   and adapt `eval_1` and `interpret_1` as
517 {h <blockquote><pre>
518        eval_2 : expr -> (value -> 'a) -> (error -> 'a) -> 'a
519   interpret_2 : program -> expressible_value
520 </pre></blockquote>}
526 function eval_2 (e:expr) (k:int -> 'a) (kerr: unit -> 'a) : 'a =
527   match e with
528     | Cte n -> if n >= 0 then k n else kerr ()
529     | Sub e1 e2 ->
530       eval_2 e1 (fun v1 ->
531          eval_2 e2 (fun v2 ->
532            if v1 >= v2 then k (v1 - v2) else kerr ())
533            kerr) kerr
534   end
537 function eval_2 (e:expr) (k:int -> 'a) (kerr: unit -> 'a) : 'a =
538   match e with
539     | Cte n -> if n >= 0 then k n else kerr ()
540     | Sub e1 e2 ->
541       eval_2 e1 (eval_2a e2 k kerr) kerr
542   end
544 with eval_2a (e2:expr) (k:int -> 'a) (kerr : unit -> 'a) : int -> 'a =
545   (fun v1 ->  eval_2 e2 (eval_2b v1 k kerr) kerr)
547 with eval_2b (v1:int) (k:int -> 'a) (kerr : unit -> 'a) : int -> 'a =
548   (fun v2 ->  if v1 >= v2 then k (v1 - v2) else kerr ())
552 function interpret_2 (p : prog) : value =
553   eval_2 p (fun n ->  Vnum n) (fun _ ->  Underflow)
556 lemma cps2_correct_expr_aux:
557   forall k: int -> 'a, e1 e2, kerr: unit -> 'a.
558   eval_2 (Sub e1 e2) k kerr = eval_2 e1 (eval_2a e2 k kerr) kerr
560 lemma cps2_correct_expr:
561   forall e, kerr: unit->'a, k:int -> 'a. eval_2 e k kerr =
562   match eval_0 e with Vnum n -> k n | Underflow -> kerr () end
564 lemma cps2_correct: forall p. interpret_2 p = interpret_0 p
566 (** {4 Exercise 1.3}
568   Specialize the codomain of the continuations and of the evaluation function
569   so that it is not polymorphic anymore but is `expressible_value`, and
570   then short-circuit the second continuation to stop in case of error
571 {h <blockquote><pre>
572        eval_3 : expr -> (value -> expressible_value) -> expressible_value
573   interpret_3 : program -> expressible_value
574 </pre></blockquote>}
575   NB: Now there is only one continuation and it is applied only in
576   absence of error.
581 function eval_3 (e:expr) (k:int -> value) : value =
582   match e with
583     | Cte n -> if n >= 0 then k n else Underflow
584     | Sub e1 e2 ->
585       eval_3 e1 (eval_3a e2 k)
586   end
588 with eval_3a (e2:expr) (k:int -> value) : int -> value =
589   (fun v1 ->  eval_3 e2 (eval_3b v1 k))
591 with eval_3b (v1:int) (k:int -> value) : int -> value =
592   (fun v2 ->  if v1 >= v2 then k (v1 - v2) else Underflow)
595 function interpret_3 (p : prog) : value = eval_3 p (fun n ->  Vnum n)
597 let rec lemma cps3_correct_expr (e:expr) : unit
598   variant { e }
599   ensures { forall k. eval_3 e k =
600     match eval_0 e with Vnum n -> k n | Underflow -> Underflow end }
601 = match e with
602   | Cte _ -> ()
603   | Sub e1 e2 ->
604       cps3_correct_expr e1;
605       cps3_correct_expr e2;
606       assert {forall k. eval_3 e k = eval_3 e1 (eval_3a e2 k) }
607   end
609 lemma cps3_correct: forall p. interpret_3 p = interpret_0 p
612 (** {4 Exercise 1.4}
613   De-functionalize the continuation of `eval_3`.
614 {h <blockquote><pre>
615          cont ::= ...
617     continue_4 : cont -> value -> expressible_value
618         eval_4 : expr -> cont -> expressible_value
619   interprete_4 : program -> expressible_value
620 </pre></blockquote>}
621   The data type `cont` represents the grammar of contexts.
622   (NB. has it changed w.r.t to previous exercise?)
624   The two mutually recursive functions `eval_4` and `continue_4`
625   implement an abstract machine, that is a transition system that
626   stops immediately in case of error, or and the end of computation.
632 type cont = I | A expr cont | B int cont
636 One would want to write in Why:
637 {h <blockquote><pre>
638 function eval_cont (c:cont) (v:value) : value =
639   match v with
640   | Underflow -> Underflow
641   | Vnum v ->
642   match c with
643   | A e2 k -> eval_cont (B (Vnum v) k) (eval_0 e2)
644   | B v1 k -> eval_cont k (if v1 >= v then Vnum (v1 - v) else Underflow)
645   | I -> Vnum v (* v >= 0 by construction *)
646   end
647 </pre></blockquote>}
648 But since the recursion is not structural, Why3 kernel rejects it
649 (definitions in the logic part of the language must be total)
651 We replace that with a relational definition, an inductive one.
655 inductive eval_cont cont value value =
656 | underflow :
657   forall k:cont. eval_cont k Underflow Underflow
658 | a1 :
659   forall e2:expr, k:cont, v:int, r:value.
660   eval_cont (B v k) (eval_0 e2) r -> eval_cont (A e2 k) (Vnum v) r
661 | a2 :
662   forall v1:int, k:cont, v:int, r:value.
663   eval_cont k (if v1 >= v then Vnum (v1 - v) else Underflow) r
664   -> eval_cont (B v1 k) (Vnum v) r
665 | a3 :
666   forall v:int. eval_cont I (Vnum v) (Vnum v)
668 (** Some functions to serve as measures for the termination proof *)
670 function size_e (e:expr) : int =
671   match e with
672   | Cte _ -> 1
673   | Sub e1 e2 -> 3 + size_e e1 + size_e e2
674   end
676 lemma size_e_pos: forall e: expr. size_e e >= 1
678 use Defunctionalization as D
680 function size_c (c:cont) : int =
681   match c with
682   | I -> 0
683   | A e2 k -> 2 + D.size_e e2 + size_c k
684   | B _ k -> 1 + size_c k
685   end
687 lemma size_c_pos: forall c: cont. size_c c >= 0
689 let rec continue_4 (c:cont) (v:int) : value
690   requires { v >= 0 }
691   variant { size_c c }
692   ensures { eval_cont c (Vnum v) result }
693   =
694   match c with
695     | A e2 k -> eval_4 e2 (B v k)
696     | B v1 k -> if v1 >= v then continue_4 k (v1 - v) else Underflow
697     | I -> Vnum v
698   end
700 with eval_4 (e:expr) (c:cont) : value
701   variant { size_c c + D.size_e e }
702   ensures { eval_cont c (eval_0 e) result }
703   =
704   match e with
705     | Cte n -> if n >= 0 then continue_4 c n else Underflow
706     | Sub e1 e2 -> eval_4 e1 (A e2 c)
707   end
709 (** The interpreter. The post-condition specifies that this program
710     computes the same thing as `eval_0` *)
712 let interpret_4 (p:prog) : value
713   ensures { result = eval_0 p }
714   = eval_4 p I
717 let test () =
718    interpret_4 p0,
719    interpret_4 p1,
720    interpret_4 p2,
721    interpret_4 p3,
722    interpret_4 p4
729 (** {2 Reduction Semantics} *)
731 module ReductionSemantics
734 A small step semantics, defined iteratively with reduction contexts
737   use Expr
738   use DirectSem
741 Expressions:
742 {h <blockquote><pre>
743 n  :  int
745 e  :  expression
746 e ::= n | e - e
748 p  :  program
749 p ::= e
750 </pre></blockquote>}
752 Values:
753 {h <blockquote><pre>
754 v  :  value
755 v ::= n
756 </pre></blockquote>}
758 Potential Redexes:
759 {h <blockquote><pre>
760   rp ::= n1 - n2
761 </pre></blockquote>}
763 Reduction Rule:
764 {h <blockquote><pre>
765   n1 - n2 -> n3
766 </pre></blockquote>}
767 (in the case of relative integers Z, all potential redexes are true redexes;
768  but for natural numbers, not all of them are true ones:
769 {h <blockquote><pre>
770    n1 - n2 -> n3   if n1 >= n2
771 </pre></blockquote>}
772 a potential redex that is not a true one is stuck.)
774 Contraction Function:
775 {h <blockquote><pre>
776   contract : redex_potentiel -> expression + stuck
777   contract (n1 - n2) = n3   si n3 = n1 - n2
778 </pre></blockquote>}
779 and if there are only non-negative numbers:
780 {h <blockquote><pre>
781   contract (n1 - n2) = n3     if n1 >= n2 and n3 = n1 - n2
782   contract (n1 - n2) = stuck  if n1 < n2
783 </pre></blockquote>}
786 predicate is_a_redex (e:expr) =
787   match e with
788   | Sub (Cte _) (Cte _) -> true
789   | _ -> false
790   end
792 let contract (e:expr) : expr
793   requires { is_a_redex e }
794   ensures { eval_0 result = eval_0 e }
795   =
796   match e with
797   | Sub (Cte v1) (Cte v2) -> Cte (v1 - v2)
798   | _ -> absurd
799   end
802 Reduction Contexts:
803 {h <blockquote><pre>
804 C  : cont
805 C ::= [] | [C e] | [v C]
806 </pre></blockquote>}
809 type context = Empty | Left context expr | Right int context
812 Recomposition:
813 {h <blockquote><pre>
814              recompose : cont * expression -> expression
815      recompose ([], e) = e
816 recompose ([C e2], e1) = recompose (C, e1 - e2)
817 recompose ([n1 C], e2) = recompose (C, n1 - e2)
818 </pre></blockquote>}
821 let rec function recompose (c:context) (e:expr) : expr =
822   match c with
823   | Empty -> e
824   | Left c e2 -> recompose c (Sub e e2)
825   | Right n1 c -> recompose c (Sub (Cte n1) e)
826   end
828 let rec lemma recompose_values (c:context) (e1 e2:expr) : unit
829   requires { eval_0 e1 = eval_0 e2 }
830   variant  { c }
831   ensures  { eval_0 (recompose c e1) = eval_0 (recompose c e2) }
832 = match c with
833   | Empty -> ()
834   | Left c e -> recompose_values c (Sub e1 e) (Sub e2 e)
835   | Right n c -> recompose_values c (Sub (Cte n) e1) (Sub (Cte n) e2)
836   end
838 (* not needed anymore
839 let rec lemma recompose_inversion (c:context) (e:expr)
840   requires {
841       match c with Empty -> false | _ -> true end \/
842       match e with Cte _ -> false | Sub _ _ -> true end }
843   variant { c }
844   ensures {  match recompose c e with
845                Cte _ -> false | Sub _ _ -> true end }
846 = match c with
847   | Empty -> ()
848   | Left c e2 -> recompose_inversion c (Sub e e2)
849   | Right n1 c -> recompose_inversion c (Sub (Cte n1) e)
850   end
854 Decomposition:
855 {h <blockquote><pre>
856 dec_or_val = (C, rp) | v
857 </pre></blockquote>}
858 Decomposition function:
859 {h <blockquote><pre>
860              decompose_term : expression * cont -> dec_or_val
861       decompose_term (n, C) = decompose_cont (C, n)
862 decompose_term (e1 - e2, C) = decompose_term (e1, [C e2])
864              decompose_cont : cont * value -> dec_or_val
865      decompose_cont ([], n) = n
866   decompose_cont ([C e], n) = decompose_term (e, [n c])
867 decompose_term ([n1 C], n2) = (C, n1 - n2)
869   decompose : expression -> dec_or_val
870 decompose e = decompose_term (e, [])
871 </pre></blockquote>}
874 exception Stuck
876 predicate is_a_value (e:expr) =
877   match e with
878   | Cte _ -> true
879   | _ -> false
880   end
882 predicate is_empty_context (c:context) =
883   match c with
884   | Empty -> true
885   | _ -> false
886   end
889 use Defunctionalization as D (* for size_e *)
891 function size_e (e:expr) : int = D.size_e e
893 function size_c (c:context) : int =
894   match c with
895   | Empty -> 0
896   | Left c e -> 2 + size_c c + size_e e
897   | Right _ c -> 1 + size_c c
898   end
900 lemma size_c_pos: forall c: context. size_c c >= 0
903 let rec decompose_term (e:expr) (c:context) : (context, expr)
904   variant { size_e e + size_c c }
905   ensures { let c1,e1 = result in
906             recompose c1 e1 = recompose c e /\
907             is_a_redex e1 }
908   raises { Stuck -> is_empty_context c /\ is_a_value e }
909   (* raises { Stuck -> is_a_value (recompose c e) } *)
910   =
911   match e with
912   | Cte n -> decompose_cont c n
913   | Sub e1 e2 -> decompose_term e1 (Left c e2)
914   end
916 with decompose_cont (c:context) (n:int) : (context, expr)
917   variant { size_c c }
918   ensures { let c1,e1 = result in
919             recompose c1 e1 = recompose c (Cte n)  /\
920             is_a_redex e1 }
921   raises { Stuck -> is_empty_context c }
922 (*  raises { Stuck -> is_a_value (recompose c (Cte n)) } *)
923   =
924   match c with
925   | Empty -> raise Stuck
926   | Left c e -> decompose_term e (Right n c)
927   | Right n1 c -> c, Sub (Cte n1) (Cte n)
928   end
930 let decompose (e:expr) : (context, expr)
931   ensures { let c1,e1 = result in recompose c1 e1 = e  /\
932             is_a_redex e1 }
933   raises { Stuck -> is_a_value e }
934   =
935   decompose_term e Empty
938 One reduction step:
939 {h <blockquote><pre>
940 reduce : expression -> expression + stuck
942 if decompose e = v
943 then reduce e = stuck
945 if decompose e = (C, rp)
946 and contract rp = stuck
947 then reduce e = stuck
949 if decompose e = (C, rp)
950 and contract rp = c
951 then reduce e = recompose (C, c)
952 </pre></blockquote>}
955 (** {4 Exercise 2.0}
956   Implement the reduction semantics above and test it
960 let reduce (e:expr) : expr
961   ensures { eval_0 result = eval_0 e }
962   raises { Stuck -> is_a_value e }
963   =
964   let c,r = decompose e in
965   recompose c (contract r)
968 Evaluation based on iterated reduction:
969 {h <blockquote><pre>
970 itere : red_ou_val -> value + erreur
972 itere v = v
974 if contract rp = stuck
975 then itere (C, rp) = stuck
977 if contract rp = c
978 then itere (C, rp) = itere (decompose (recompose (C, c)))
979 </pre></blockquote>}
983 let rec itere (e:expr) : int
984   diverges (* termination could be proved but that's not the point *)
985   ensures { eval_0 e = result }
986   =
987   match reduce e with
988   | e' -> itere e'
989   | exception Stuck ->
990      match e with
991      | Cte n -> n
992      | _ -> absurd
993      end
994   end
996 (** {4 Exercise 2.1}
997   Optimize the step recomposition / decomposition
998   into a single function `refocus`.
1002 let refocus c e
1003   ensures { let c1,e1 = result in
1004             recompose c1 e1 = recompose c e /\
1005             is_a_redex e1 }
1006   raises { Stuck -> is_empty_context c /\ is_a_value e }
1007 = decompose_term e c
1009 let rec itere_opt (c:context) (e:expr) : int
1010   diverges
1011   ensures { result = eval_0 (recompose c e) }
1012 = match refocus c e with
1013   | c, r -> itere_opt c (contract r)
1014   | exception Stuck ->
1015      assert { is_empty_context c };
1016      match e with
1017      | Cte n -> n
1018      | _ -> absurd
1019      end
1020   end
1022 let rec normalize (e:expr)
1023   diverges
1024   ensures { result = eval_0 e }
1025 = itere_opt Empty e
1030 (** {4 Exercise 2.2}
1031   Derive an abstract machine
1035 {h <blockquote><pre>
1036 (c,Cte n)_1 -> (c,n)_2
1037 (c,Sub e1 e2)_1 -> (Left c e2,e1)_1
1038 (Empty,n)_2 -> stop with result = n
1039 (Left c e,n)_2 -> (Right n c,e)_1
1040 (Right n1 c,n)_2 -> (c,Cte (n1 - n))_1
1041 </pre></blockquote>}
1044 let rec eval_1 c e
1045   variant { 2 * (size_c c + size_e e) }
1046   ensures { result = eval_0 (recompose c e) }
1047 = match e with
1048   | Cte n -> eval_2 c n
1049   | Sub e1 e2 -> eval_1 (Left c e2) e1
1050   end
1052 with eval_2 c n
1053   variant { 1 + 2 * size_c c }
1054   ensures { result = eval_0 (recompose c (Cte n)) }
1055 = match c with
1056   | Empty -> n
1057   | Left c e -> eval_1 (Right n c) e
1058   | Right n1 c -> eval_1 c (Cte (n1 - n))
1059   end
1061 let interpret p
1062   ensures { result = eval_0 p }
1063 = eval_1 Empty p
1065 let test () =
1066    interpret p0,
1067    interpret p1,
1068    interpret p2,
1069    interpret p3,
1070    interpret p4
1077 module RWithError
1079 use Expr
1080 use SemWithError
1082 (** {4 Exercise 2.3}
1083   An abstract machine for the case with errors
1088 {h <blockquote><pre>
1089 (c,Cte n)_1 -> stop on Underflow if  n < 0
1090 (c,Cte n)_1 -> (c,n)_2 if n >= 0
1091 (c,Sub e1 e2)_1 -> (Left c e2,e1)_1
1092 (Empty,n)_2 -> stop on Vnum n
1093 (Left c e,n)_2 -> (Right n c,e)_1
1094 (Right n1 c,n)_2 -> stop on Underflow if n1 < n
1095 (Right n1 c,n)_2 -> (c,Cte (n1 - n))_1 if n1 >= n
1096 </pre></blockquote>}
1100 type context = Empty | Left context expr | Right int context
1102 use Defunctionalization as D (* for size_e *)
1104 function size_e (e:expr) : int = D.size_e e
1106 function size_c (c:context) : int =
1107   match c with
1108   | Empty -> 0
1109   | Left c e -> 2 + size_c c + size_e e
1110   | Right _ c -> 1 + size_c c
1111   end
1113 lemma size_c_pos: forall c: context. size_c c >= 0
1115 function recompose (c:context) (e:expr) : expr =
1116   match c with
1117   | Empty -> e
1118   | Left c e2 -> recompose c (Sub e e2)
1119   | Right n1 c -> recompose c (Sub (Cte n1) e)
1120   end
1122 let rec lemma recompose_values (c:context) (e1 e2:expr) : unit
1123   requires { eval_0 e1 = eval_0 e2 }
1124   variant  { c }
1125   ensures  { eval_0 (recompose c e1) = eval_0 (recompose c e2) }
1126 = match c with
1127   | Empty -> ()
1128   | Left c e -> recompose_values c (Sub e1 e) (Sub e2 e)
1129   | Right n c -> recompose_values c (Sub (Cte n) e1) (Sub (Cte n) e2)
1130   end
1132 let rec lemma recompose_underflow (c:context) (e:expr) : unit
1133   requires { eval_0 e = Underflow }
1134   variant { c }
1135   ensures { eval_0 (recompose c e) = Underflow }
1136 = match c with
1137   | Empty -> ()
1138   | Left c e1 -> recompose_underflow c (Sub e e1)
1139   | Right n c -> recompose_underflow c (Sub (Cte n) e)
1140   end
1144 let rec eval_1 c e
1145   variant { 2 * (size_c c + size_e e) }
1146   ensures { result = eval_0 (recompose c e) }
1147 = match e with
1148   | Cte n -> if n >= 0 then eval_2 c n else Underflow
1149   | Sub e1 e2 -> eval_1 (Left c e2) e1
1150   end
1152 with eval_2 c n
1153   variant { 1 + 2 * size_c c }
1154   requires { n >= 0 }
1155   ensures { result = eval_0 (recompose c (Cte n)) }
1156 = match c with
1157   | Empty -> Vnum n
1158   | Left c e -> eval_1 (Right n c) e
1159   | Right n1 c -> if n1 >= n then eval_1 c (Cte (n1 - n)) else Underflow
1160   end
1162 let interpret p
1163   ensures { result = eval_0 p }
1164 = eval_1 Empty p
1166 let test () =
1167    interpret p0,
1168    interpret p1,
1169    interpret p2,
1170    interpret p3,
1171    interpret p4