Make some more progress on the UCC evaluator article

This commit is contained in:
Danila Fedorin 2021-11-28 01:48:01 -08:00
parent 6c1940f5d2
commit 4d24e7095b

View File

@ -265,10 +265,61 @@ eval_chain nil (e_comp (e_comp true false) or) (true :: nil)
This is the type of sequences (or chains) of steps corresponding to the This is the type of sequences (or chains) of steps corresponding to the
evaluation of the program \\(\\text{true}\\ \\text{false}\\ \\text{or}\\), evaluation of the program \\(\\text{true}\\ \\text{false}\\ \\text{or}\\),
starting in an empty stack and evaluating to a stack with only true on top. starting in an empty stack and evaluating to a stack with only
Thus to say that an expression evaluates to some final stack `vs'`, in \\(\\text{true}\\) on top. Thus to say that an expression evaluates to some
_some unknown number of steps_, it's sufficient to write: final stack `vs'`, in _some unknown number of steps_, it's sufficient to write:
```Coq ```Coq
eval_chain vs e vs' eval_chain vs e vs'
``` ```
Evaluation chains have a couple of interesting properties. First and foremost,
they can be "concatenated". This is analagous to the `Sem_e_comp` rule
in our original semantics: if an expression `e1` starts in stack `vs`
and finishes in stack `vs'`, and another expression starts in stack `vs'`
and finishes in stack `vs''`, then we can compose these two expressions,
and the result will start in `vs` and finish in `vs''`.
{{< codelines "Coq" "dawn/DawnEval.v" 69 75 >}}
The proof is very short. We go
{{< sidenote "right" "concat-note" "by induction on the left evaluation" >}}
It's not a coincidence that defining something like <code>(++)</code>
(list concatenation) in Haskell typically starts by pattern matching
on the <em>left</em> list. In fact, proofs by <code>induction</code>
actually correspond to recursive functions! It's tough putting code blocks
in sidenotes, but if you're playing with the
<a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/dawn/DawnEval.v"><code>DawnEval.v</code></a> file, try running
<code>Print eval_chain_ind</code>, and note the presence of <code>fix</code>,
the <a href="https://en.wikipedia.org/wiki/Fixed-point_combinator">fixed point
combinator</a> used to implement recursion.
{{< /sidenote >}}
chain (the one for `e1`). Coq takes care of most of the rest with `auto`.
The key to this proof is that whatever `P` is cotained within a "node"
in the left chain determines how `eval_step (e_comp e1 e2)` behaves. Whether
`e1` evaluates to `final` or `middle`, the composition evaluates to `middle`
(a composition of two expressions cannot be done in one step), so we always
{{< sidenote "left" "cons-note" "create a new \"cons\" node." >}}
This is <em>unlike</em> list concatenation, since we typically don't
create new nodes when appending to an empty list.
{{< /sidenote >}} via `chain_middle`. Then, the two cases are as follows:
* If the step was `final`, then
the rest of the steps use only `e2`, and good news, we already have a chain
for that!
* Otherwise, the step was `middle`, an we stll have a chain for some
intermediate program `ei` that starts in some stack `vsi` and ends in `vs'`.
By induction, we know that _this_ chain can be concatenated with the one
for `e2`, resulting in a chain for `e_comp ei e2`. All that remains is to
attach to this sub-chain the one step from `(vs, e1)` to `(vsi, ei)`, which
is handled by `chain_middle`.
{{< todo >}}A drawing would be nice here!{{< /todo >}}
The `merge` operation is reversible; chains can be split into two pieces,
one for each composed expression:
{{< codelines "Coq" "dawn/DawnEval.v" 77 78 >}}
While interesting, this particular fact isn't used anywhere else in this
post, and I will omit the proof here.