-
Notifications
You must be signed in to change notification settings - Fork 0
/
agda.lagda
1498 lines (1305 loc) · 66 KB
/
agda.lagda
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
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
\chapter{Machine-Assisted Proofs in Agda}\label{ch:agda}
To give a formal proof of the correctness property posited in the previous
chapter, we may make use of a mechanised proof assistant.
Agda~\cite{norell07-thesis,theagdateam10-wiki} is a dependently-typed
functional programming language based on Martin-L\"{o}f intuitionistic type
theory~\cite{martin-lof80-itt,nordstrom90-program}. Via the Curry-Howard
correspondence---that is, viewing types as propositions and programs as
proofs---it is also used as a proof assistant for constructive mathematics.
In this chapter, we shall provide a gentle introduction to the language, and
demonstrate how we can formalise statements of compiler correctness by means
of machine-checked proofs, culminating in a verified formalisation of the
proofs of chapter \ref{ch:semantics}.
\section{Introduction to Agda}%{{{%
%{{{%
%if False
\begin{code}
module agda where
private
module ignore where
\end{code}
%endif
%}}}%
%{{{%
The current incarnation of Agda has a syntax similar to that of Haskell, and
should look familiar to readers versed in the latter. As in previous
chapters, we will adopt a colouring convention for ease of readability:
\begin{longtable}{l||l}
Syntactic Class & Examples \\
\hline
Keywords & |data|, |where|, |with|\ldots \\
Types & |ℕ|, |List|, |Set|\ldots \\
Constructors & |zero|, |suc|, |tt|, |[]|\ldots \\
Functions & |id|, |_+_|, |Star.gmap|\ldots
%\\
%Literals & |0|, |1|, |"hello world"|\ldots
\end{longtable}
\noindent Semantically, Agda is distinguished by its foundation on
\emph{dependent types}, and is closely related to systems such as
Epigram~\cite{mcbride08-epigram,mcbride05-epigram-notes} and
Coq~\cite{the-coq-development-team08-website}. Dependent types systems are
so-called because they allow for types to depend on values, in addition to
the usual parametrisation by other types as seen in languages such as
Haskell. This provides us with a much richer vocabulary of discourse for not
only stating the properties of our programs, but also to be able to prove
such properties within the same system. We will begin to explore how this is
facilitated by dependent types from section \ref{sec:agda-curryhoward}
onwards.
%In an informal sense, generalised abstract data types (GADTs)
%in recent implementations of Haskell allow data types to be indexed on their
%argument types, in a similar fashion.
%}}}%
\subsection{Data and Functions}%{{{%
We start our introduction to Agda with some simple data and function
definitions. The language itself does not specify any primitive data types,
and it serves as a good introduction to see how some of these may be defined
in its standard library~\cite{danielsson10-stdlib}. For example, we may
define the Peano numbers as follows:
\begin{code}
data ℕ : Set where
zero : ℕ
suc : ℕ → ℕ
\end{code}
This is syntactically similar to Haskell's generalised abstract data type
(GADT) declarations~\cite{peyton-jones04-gadt} with a few minor differences.
Firstly, arbitrary Unicode characters may be used in identifiers, and we do
not use upper and lower case letters to distinguish between values and
constructors\footnote{The implication here is that the processes of syntax
highlighting and type-checking are inextricably linked, and that syntax
colours provides more information for the reader.}. Secondly, we write |:|
to mean \emph{has-type-of}, and write |Set| for the \emph{type of
types}\footnote{Agda in fact has countably infinite levels of |Set|s, with
$|Set : Set₁ : Set₂ : |\ldots$. This stratification prevents the formation
of paradoxes that would lead to inconsistencies in the system.}.
% FIXME: cite Russell?
Thus, the above defines |ℕ| as a new data type inhabiting |Set|, with
a nullary constructor |zero| as the base case and an unary constructor |suc|
as the inductive case. These correspond to two of the Peano axioms that
define the natural numbers: |zero| is a natural number, and every number |n|
has a successor |suc n|.
We may define addition on the natural numbers as follows, by induction on
its first argument:
\begin{code}
_+_ : ℕ → ℕ → ℕ
zero + n = n
suc m + n = suc (m + n)
\end{code}
Agda allows the use of arbitrary mixfix operators, where underscores
`$\func{\anonymous}$' denote the positions of arguments. Another difference
is that all functions in Agda must be total. For example, omitting the
|zero| case of |_+_| would lead to an error during the typechecking process,
rather than a warning as in Haskell. Additionally, only structural recursion
is permitted. In the definition of |_+_| above, we recurse on the definition
of |ℕ|: the first argument is strictly smaller on the right hand side,
i.e.~|m| rather than |suc m|. While more general forms of recursion are
possible, Agda requires us to explicitly prove that the resulting
definitions are total.
%}}}%
\subsection{Programs as Proofs and Types as Predicates}\label{sec:agda-curryhoward}%{{{%
The Curry-Howard correspondence refers to the initial observation by Curry
that types---in the sense familiar to functional programmers---correspond to
axiom-schemes for intuitionistic logic, while Howard later noted that proofs
in formal systems such as natural deduction can be directly interpreted as
terms in a model of computation such as the typed lambda calculus.
%incompleteness theorems ~\cite{godel?}
The intuitionistic approach to logic only relies on constructive methods,
disallowing notions from classical logic such as the law of the excluded
middle ($P \vee \lnot P$) or double-negation elimination ($\lnot \lnot
P \rightarrow P$). For example, intuitionist reject $P \vee \lnot P$ because
there exists a statement $P$ in any sufficiently powerful logic that can
neither be proved nor disproved within the system, by G\"{o}del's
incompleteness theorems. In other words, intuitionism equates the truth of
a statement $P$ with the possibility of constructing a proof object that
satisfies $P$, therefore a proof of $\lnot \lnot P$, refuting the
non-existence of $P$, does not imply $P$ itself.
What does this mean for the proletarian programmer? Under the Curry-Howard
correspondence, the type |A -> B| is interpreted as the logical statement
`\emph{|A| implies |B|}', and vice-versa. Accordingly, a program |p : A ->
B| corresponds to a proof of `\emph{|A| implies |B|}', in that executing |p|
constructs a witness of |B| as output, given a witness of |A| as input. Thus
in a suitable type system, programming is the same as constructing proofs in
a very concrete sense.
In a traditional strongly typed programming language such as Haskell, the
type system exists to segregate values of different types. On the other
hand, distinct values of the same type all look the same to the
type-checker, which means we are unable to form types corresponding to
propositions about particular values. Haskell's GADTs break down this
barrier in a limited sense, by allowing the constructors of a parametric
type to target particular instantiations of the return type. While this
allows us to exploit a Haskell type checker that supports GADTs as
a proof-checker in some very simple cases, it comes at the cost of requiring
`counterfeit type-level copies of data'~\cite{mcbride02-faking}.
%}}}%
\subsection{Dependent Types}\label{sec:agda-dependent}%{{{%
%FIXME: martin-lof-1971
Dependent type systems follow a more principled approach, being founded on
Martin-L\"of intuitionistic type theory~\cite{nordstrom90-program}. These
have been studied over several decades, and the current incarnation of
Agda~\cite{norell07-thesis,theagdateam10-wiki} is one example of such
a system.
Coming from a Hindley-Milner background, the key distinction of dependent
types is that values can influence the types of other, subsequent values.
Let us introduce the notion by considering an example of a dependent type:
%format fz = "\cons{fz}"
%format fs = "\cons{fs}"
\begin{code}
data Fin : ℕ → Set where
fz : {n : ℕ} → Fin (suc n)
fs : {n : ℕ} → Fin n → Fin (suc n)
\end{code}
This defines a data type |Fin|---similar to |ℕ| above---that is additionally
\emph{indexed} by a natural number. Its two constructor are analogues of the
|zero| and |suc| of |ℕ|. The |fz| constructor takes an argument of type |ℕ|
named |n|, that is referred to in its resultant type of |Fin (suc n)|. The
braces |{| and |}| indicate that |n| is an implicit parameter, which we may
omit at occurrences of |fz|, provided that the value of |n| can be
automatically inferred. We can see how this might be possible in the case of
the |fs| constructor: its explicit argument has type |Fin n|, from which we
may deduce |n|.
The above |Fin| represents a family of types, where each type |Fin n| has
exactly |n| distinct values. This is apparent in the resultant types of the
constructors, for example: neither |fz| and |fs| can inhabit |Fin zero|, as
they both target |Fin (suc n)| for some |n|. The only inhabitant of |Fin
(suc zero)| is |fz|, since |fs| requires an argument of type |Fin zero|,
which would correspond to a proof that |Fin zero| is inhabited.
A routine application of the |Fin| family is in the safe lookup of lists or
vectors, where a static guarantee on the bounds of the given position is
required. The following definition defines the type of vectors, indexed by
their length:
\begin{code}
data Vec (X : Set) : ℕ → Set where
[] : Vec X zero
_∷_ : {n : ℕ} → X → Vec X n → Vec X (suc n)
\end{code}
Unsurprisingly the empty list |[]| corresponds to a |Vec X| of length
|zero|, while the |_∷_| constructor prepends an element of |X| to an
existing |Vec X| of length |n|, to give a |Vec X| of length |suc n|.
%format lookup = "\func{lookup}"
%format n′ = "n\Prime{}"
A |lookup| function can then be defined as follows:
\savecolumns
\begin{code}
lookup : {X : Set} {n : ℕ} → Fin n → Vec X n → X
lookup fz (x ∷ xs) = x
lookup (fs i) (x ∷ xs) = lookup i xs
\end{code}
The first argument gives the position in the vector where we wish to extract
an element, with the second argument being the vector itself.
Earlier we mentioned that all definitions in Agda must be total, yet the
|lookup| function seemingly does not consider the case of the empty vector
|[]|. This is because in a dependently-typed language, pattern matching on
one argument can potentially influence the types of other arguments:
matching on either |fz| or |fs i| forces |n| to |suc n′| for some |n′|, and
in both cases the type of the vector is refined to |Vec X (suc n′)|. As |[]|
can only inhabit |Vec X zero|, we needn't explicitly list this case. Had we
pattern matched the vector with |[]| first on the other hand, the type of
the position then becomes |Fin zero|, which is uninhabited. In this case, we
may use the `impossible' pattern |()| for the first argument, to indicate
that the case is unreachable:
\restorecolumns
\begin{spec}
lookup () []
\end{spec}
In such cases, the right hand side of the definition is simply omitted.
This is only an elementary demonstration of the power of dependent types.
Being able to form any proposition in intuitionistic logic as a type gives
us a powerful vocabulary with which to state and verify the properties of
our programs. Conversely, by interpreting a type as its corresponding
proposition, we have mapped the activity of constructing mathematical proofs
to `just' programming, albeit in a more rigorous fashion than usual.
%}}}%
%2010/04/14: [01:30] <pigworker> They have names. "Martin-Löf equality"
%takes just the set as as the parameter; "Paulin-Mohring equality" takes the
%set and one of the elements as parameter.
\subsection{Equality and its Properties}%{{{%
The previous section introduced dependent types from a programming and data
types point-of-view. We shall now take a look at how we can use dependent
types to state logical propositions and to construct their proofs.
A simple and commonplace construct is that of an equality type,
corresponding to the proposition that two elements of the same type are
definitionally equal. The following definition encodes the Martin-L\"of
equality relation:
%format _≡_ = "\type{\anonymous{\equiv}\anonymous}"
%format refl = "\cons{refl}"
\savecolumns
\begin{code}
data _≡_ {X : Set} : X → X → Set where
refl : {x : X} → x ≡ x
\end{code}
Agda does not provide the kind of Hindley-Milner polymorphism as seen in
Haskell, although we can simply take an additional parameter of the type we
wish to be polymorphic over. In the above definition of equality, the
variable |X| corresponds to the type of the underlying values, which can
often be inferred from the surrounding context. By marking this parameter as
implicit, we effectively achieve the same end result in its usage.
The above definition of |_≡_| is indexed by two explicit parameters of type
|X|. Its sole constructor is |refl|, that inhabits the type |x ≡ x| given an
argument |x : X|. Logically, |refl| corresponds to the axiom of reflexivity
$\forall x.\ x \equiv x$. In cases where the type of the argument---such as
|x| here---can be inferred form the surrounding context, the |∀| keyword
allows us to write the type in a way that better resembles the corresponding
logical notation, for example:
\restorecolumns
\begin{spec}
refl : ∀ {x} → x ≡ x
\end{spec}
In general, Agda syntax allows us to write `$\anonymous$' in place of
expressions---including types---that may be automatically inferred. The
above is in fact syntactic sugar for the following:
\restorecolumns
\begin{spec}
refl : {x : _} → x ≡ x
\end{spec}
Agda includes an interactive user interface for the
Emacs~\cite{stallman10-emacs} operating system that supports incremental
development by the placement of `holes' where arbitrary expressions are
expected. Incomplete programs with holes can be passed to the type checker,
which then informs the user of the expected type. Thus, writing proofs in
Agda typically involves a two-way dialogue between the user and the type
checker.
%format sym = "\func{sym}"
%format x≡y = "x{\equiv}y"
Given the above definition of reflexivity as an axiom, we may go on to prove
that |≡| is also symmetric and transitive. The former is the proposition
that given any |x| and |y|, a proof of |x ≡ y| implies that |y ≡ x|. This is
implemented as the following |sym| function,
\def\symHole{|sym x y x≡y = ?|}
\begin{code}
sym : {X : Set} (x y : X) → x ≡ y → y ≡ x
{-"\symHole"-}
\end{code}
where the `|?|' mark indicates an as-yet unspecified expression. Multiple
arguments of the same type are simply separated with spaces, while arrows
between pairs of named arguments can be omitted, since there cannot be any
ambiguity.
Successfully type-checking the above turns the `|?|' into a \emph{hole}
$\shed{\{~\}}$, inside which we may further refine the proof. The expected
type of the hole is displayed, and we can ask the type-checker to enumerate
any local names that are in scope. In this case, the hole is expected to be
of type |y ≡ x|, with the arguments |x y : X| and |x≡y : x ≡ y| in scope.
At this point, we can ask the system to perform case-analysis on |x≡y|.
Being an equality proof, this argument must be the |refl| constructor. But
something magical also happens during the case-split operation:
\begin{spec}
sym x .x refl = {-"\shed{\{~\}}"-}
\end{spec}
Since |refl| only inhabits the type |x ≡ x|, the type checker concludes that
the first two arguments |x| and |y| must be the same, and rewrites the
second as |.x| to reflect this fact. Whereas pattern matching in Haskell is
an isolated affair, in a dependently typed context it can potentially cause
interactions with other arguments, revealing more information about the them
that can be checked and enforced by the system.
Accordingly, |y| is no longer in-scope, and the goal type becomes |x ≡ x|,
which is satisfied by |refl|:
\begin{code}
sym x .x refl = refl
\end{code}
%format sym' = sym
As we do not explicitly refer to |x| on the right hand side, we could have
made the |x| and |y| arguments implicit too, leading to a more succinct
definition:
\begin{code}
sym' : {X : Set} {x y : X} → x ≡ y → y ≡ x
sym' refl = refl
\end{code}
We prove transitivity in a similar fashion,
%format trans = "\func{trans}"
\begin{code}
trans : ∀ {X : Set} {x y z : X} → x ≡ y → y ≡ z → x ≡ z
trans refl refl = refl
\end{code}
where pattern-matching the first explicit argument with |refl| unifies |y|
with |x|, refining the type of the second argument to |x ≡ z|; in turn,
matching this with |refl| then unifies |z| with |x|. The resulting goal of
|x ≡ x| is met on the right hand side with simply |refl|.
%}}}%
\subsection{Existentials and Dependent Pairs}%{{{%
In the previous section, we had already surreptitiously modelled the
universal quantifier $\forall$ as dependent functions---that is, functions
where values of earlier arguments may influence later types. Dually, we can
model the existential quantifier $\exists$ using \emph{dependent pairs}.
This is typically defined in terms of the |Σ| type\footnote{In this thesis,
I deviate from the Agda standard library by writing |_∧_| instead of
$\cons{\anonymous{,}\anonymous}$, to avoid excessive visual overloading.}:
\begin{code}
data Σ (X : Set) (P : X → Set) : Set where
_∧_ : (x : X) → (p : P x) → Σ X P
\end{code}
We can interpret the type |Σ X (λ x → P x)| as the existentially quantified
statement that $\exists x \in X.\ P(x)$. Correspondingly, a proof of this
comprises a pair |x ∧ p|, where the latter is a proof of the proposition |P
x|. Unlike classical proofs of existence which may not necessarily be
constructive, a proof of the existence of some |x| satisfying |P|
necessarily requires us to supply such an |x|. Conversely, we can always
extract an |x| given such a proof.
As |X| can often be inferred from the type of the predicate |P|, we may
define a shorthand |∃| that accepts |X| as an implicit argument:
\begin{code}
∃ : {X : Set} (P : X → Set) → Set
∃ {X} = Σ X
\end{code}
The above definition of |∃| allows us to write |∃ λ x → P x|, which better
resembles the corresponding logical proposition of $\exists x.\ P(x)$.
Of course, the predicate |P| need not necessarily depend on the first
element of a pair. In such cases, the resulting type is a non-dependent
pair, which corresponds to a logical conjunction. This can be recovered as
a degenerate case of the above |Σ| type, in which we simply ignore the value
of the first type:
\begin{code}
_×_ : Set → Set → Set
X × Y = Σ X (λ _ → Y)
\end{code}
%\noindent Logical disjunction on the other hand corresponds to a sum type,
%sometimes called a tagged-union.
%\begin{code}
% data _⊎_ (X Y : Set) : Set where
% inj₁ : (x : X) → X ⊎ Y
% inj₂ : (y : Y) → X ⊎ Y
%\end{code}
%if False
\begin{code}
infix 4 _≡_
infixr 4 _∧_
infixr 5 _++_ _∷_
_++_ : ∀ {X m n} → Vec X m → Vec X n → Vec X (m + n)
_++_ [] ys = ys
_++_ (x ∷ xs) ys = x ∷ xs ++ ys
\end{code}
%endif
%format splitAt = "\func{splitAt}"
\noindent Putting the above into practice, we present below a definition of
|splitAt| that splits a vector of length |m + n| at position |m| into left
and right vectors of length |m| and |n| respectively. Unlike the eponymous
Haskell function, we can also witness that the contatenation of the
resulting vectors coincides with the input:
\begin{code}
splitAt : (m : ℕ) {n : ℕ} {X : Set} (xs : Vec X (m + n)) →
Σ (Vec X m) λ ys → Σ (Vec X n) λ zs → xs ≡ ys ++ zs
splitAt zero xs = [] ∧ xs ∧ refl
\end{code}
For the base case where the position is |zero|, we simply return the empty
list as the left part and the entirety of |xs| as the right. Since |[] ++
xs| reduces to just |xs|, a simple appeal to reflexivity completes this
clause.
In the inductive case of |suc m|, the input vector must contain at least one
element, followed by |xs|. We wish to split |xs| recursively at |m| and
prepend |x| to the left result. In Agda, we can pattern match on
intermediate results using the magic `|with|' keyword, which could be
thought of as the dependently-typed analogue to Haskell's $\keyw{case}$:
\begin{code}
splitAt (suc m) (x ∷ xs) with splitAt m xs
splitAt (suc m) (x ∷ .(ys ++ zs)) | ys ∧ zs ∧ refl = x ∷ ys ∧ zs ∧ refl
\end{code}
When we case-split on the proof of |xs ≡ ys ++ zs|, Agda sees that |≡.refl|
is the only possible constructor, and correspondingly rewrites the tail of
the input vector to the dotted pattern |.(ys ++ zs)|. This is simply a more
sophisticated instance of what we saw when case-splitting |sym| in the
previous section.
%}}}%
\subsection{Reflexive Transitive Closure}%{{{%
% \cite{McBride/Norell/Jansson}
Before we conclude this brief introduction to Agda, we shall introduce the
\emph{reflexive transitive closure} of McBride, Norell and
Jansson~\cite{mcbride07-star}, which generalises the notion of sequences in
a dependently-typed context. This general construct will prove useful later
when working with sequences of small-step reductions.
We begin by defining binary relations parametrised on their underlying
types:
\begin{code}
Rel : Set → Set₁
Rel X = X → X → Set
\end{code}
|Rel| has |Set₁| as its codomain in order to avoid the |Set : Set|
inconsistency. We use the |Rel X| shorthand to denote the type of binary
relations on |X|. In fact, our earlier definition of propositional equality
could equivalently have been written as follows:
\begin{spec}
data _≡_ {X : Set} : Rel X where
refl : {x : X} → x ≡ x
\end{spec}
\noindent The definition of |Star| is parametrised on a set of indices |I|
and a binary relation |R| on |I|. The type |Star {I} R| is itself another
binary relation, indexed on the same |I|:
\begin{code}
data Star {I : Set} (R : Rel I) : Rel I where
ε : {i : I} → Star R i i
_◅_ : {i j k : I} → (x : R i j) → (xs : Star R j k) → Star R i k
\end{code}
Here, |ε| gives a trivial witness of reflexivity for any |i|. The |_◅_|
constructor provides a heterogeneous definition of transitivity, accepting
a proof of |R i j| and an |R|-chain relating |j| and |k| as its two
arguments. Thus |Star R| defines the type of the reflexive transitive
closure of |R|. Being data, we may take apart an element of |Star R i k| and
inspect each step of the |R|-chain.
Alternatively the constructors |ε| and |_◅_| could be thought of as
generalisations of the \emph{nil} and \emph{cons} constructors of the list
data type for proofs of the form |R i k|, with the constraint that two
adjacent elements |x : R i j| and |y : R j k| must share a common index |j|.
%if False
\begin{code}
infixr 5 _◅_
record ⊤ : Set where
constructor tt
\end{code}
%endif
%format List′ = "\func{List}"
In the degenerate case of a constant relation |(λ _ _ → X) : Rel ⊤| whose
indices provide no extra information, we recover the usual definition of
lists of elements of type |X|:
\begin{code}
List′ : Set → Set
List′ X = Star (λ _ _ → X) tt tt
\end{code}
Here |tt| is the unique constructor of the unit type |⊤|, with |ε| and |_◅_|
taking the r\^oles of \emph{nil} and \emph{cons} respectively. For example,
we could write the two-element list of 0 and 1 as follows:
%format two = "\func{two}"
\begin{code}
two : List′ ℕ
two = zero ◅ suc zero ◅ ε
\end{code}
As it is a generalisation of lists, |Star| admits many of the usual
list-like functions. For example, while the |_◅◅_| function below provides
a proof of transitivity for |Star R|, it could equally be considered the
generalisation of \emph{append} on lists, as suggested by the structure of
its definition:
\begin{code}
_◅◅_ : {I : Set} {R : Rel I} {i j k : I} →
Star R i j → Star R j k → Star R i k
ε ◅◅ ys = ys
(x ◅ xs) ◅◅ ys = x ◅ (xs ◅◅ ys)
\end{code}
Similarly, we can define an analogue of \emph{map} on lists:
%format I′ = "I\Prime{}"
%format gmap = "\func{gmap}"
\begin{code}
gmap : {I I′ : Set} {R : Rel I} {S : Rel I′} (f : I → I′) →
( {x y : I} → R x y → S (f x) (f y)) →
{i j : I} → Star R i j → Star S (f i) (f j)
gmap f g ε = ε
gmap f g (x ◅ xs) = g x ◅ gmap f g xs
\end{code}
As well as a function |g| that is mapped over the individual elements of the
sequence, |gmap| also takes a function |f| that allows for the indices to
change.
%format <₁ = "\infix{\type{<_1}}"
%format _<₁_ = "\type{\anonymous{<_1}\anonymous}"
%format lt₁ = "\cons{lt_1}"
To conclude, let us consider a simple use case for |Star|. Take the
\emph{predecessor} relation on natural numbers, defined as |_<₁_| below:
\begin{code}
data _<₁_ : Rel ℕ where
lt₁ : (n : ℕ) → n <₁ suc n
\end{code}
Here, |lt₁ n| witnesses |n| as the predecessor of |suc n|. We can then
conveniently define the reflexive transitive closure of |_<₁_| as follows:
%format _≤_ = "\func{\anonymous{\le}\anonymous}"
%format ≤ = "\infix{\func{\le}}"
\begin{code}
_≤_ : Rel ℕ
_≤_ = Star _<₁_
\end{code}
Of course, this is just the familiar \emph{less than or equal} relation, for
which we can easily prove some familiar properties, such as the following:
%format z≤n = "\func{0{\le}n}"
\begin{code}
z≤n : (n : ℕ) → zero ≤ n
z≤n zero = ε
z≤n (suc n) = z≤n n ◅◅ (lt₁ n ◅ ε)
\end{code}
Note that a proof of |zero ≤ n| as produced by the above |z≤n| function
actually consists of a \emph{sequence} of proofs in the style of ``\emph{0
is succeeded by 1, which is succeeded by 2, \ldots{} which is succeeded by
|n|}'' that can be taken apart and inspected one by one. We will be doing
exactly this when considering reduction sequences in our later proofs.
%}}}%
%}}}%
%{{{%
%if False
\begin{code}
import Level
open import Common
\end{code}
%endif
%}}}%
\section{Agda for Compiler Correctness}%{{{%
In this section, we shall revisit the language of numbers and addition from
chapter~\ref{ch:semantics}, and demonstrate how the previous compiler
correctness result can be formalised using Agda.
\subsection{Syntax and Semantics}%{{{%
As in chapter \ref{ch:model}, we can encode the syntax of our language as
a simple algebraic data type:
%if False
\begin{code}
infixl 4 _⊕_
infix 5 #_
\end{code}
%endif
%format #_ = "\cons{\texttt{\#}\anonymous}"
%format # = "\prefix{\cons{\texttt{\#}}}"
%format _⊕_ = "\cons{\anonymous{\oplus}\anonymous}"
%format ⊕ = "\infix{\cons{\oplus}}"
%format Expression = "\type{Expression}"
\begin{code}
data Expression : Set where
#_ : (m : ℕ) → Expression
_⊕_ : (a b : Expression) → Expression
\end{code}
The two constructors |#_| and |_⊕_| correspond to the
\eqName{Exp-$\mathbb{N}$} and \eqName{Exp-$\oplus$} rules in our original
definition of the |Expression| language in chapter \ref{ch:semantics}.
%format ⟦_⟧ = "\func{[\![\anonymous]\!]}"
%format ⟦ = "\prefix{\func{[\![}}"
%format ⟧ = "\postfix{\func{]\!]}}"
Its denotational semantics---being a mapping from the syntax to the
underlying domain of $\mathbb{N}$---can be simply implemented as a function:
\begin{code}
⟦_⟧ : Expression → ℕ
⟦ # m ⟧ = m
⟦ a ⊕ b ⟧ = ⟦ a ⟧ + ⟦ b ⟧
\end{code}
The two cases in the definition of |⟦_⟧| correspond to \eqName{denote-val}
and \eqName{denote-plus} respectively. A numeric expression is interpreted
as just its value, while the |⊕| operator translates to the familiar |+| on
natural numbers.
%format _⇓_ = "\type{\anonymous{\Downarrow}\anonymous}"
%format ⇓ = "\infix{\type{{\Downarrow}}}"
%format ⇓-ℕ = "\cons{{\Downarrow}\text-\mathbb{N}}"
%format ⇓-⊕ = "\cons{{\Downarrow}\text-\oplus}"
We had previously modelled the big-step semantics of our language as
a binary relation $\Downarrow$ between expressions and numbers. In Agda,
such a relation can be implemented as a dependent data type, indexed on
|Expression| and |ℕ|:
%if False
\begin{code}
infix 4 _⇓_
\end{code}
%endif
\begin{code}
data _⇓_ : REL Expression ℕ Level.zero where
⇓-ℕ : ∀ {m} → # m ⇓ m
⇓-⊕ : ∀ {a b m n} → a ⇓ m → b ⇓ n → a ⊕ b ⇓ m + n
\end{code}
The |⇓-ℕ| constructor corresponds to the \eqName{big-val} rule: an
expression |# m| evaluates to just the value |m|. The |⇓-⊕| constructor
takes two arguments of type |a ⇓ m| and |b ⇓ n|, corresponding to the two
premises of \eqName{big-plus}.
%%{{{%
%%if False
%\begin{code}
%-- ⊕≡e⇒∄m≡e : ∀ {a b e} → a ⊕ b ≡ e → ∄ λ m → # m ≡ e
%-- ⊕≡e⇒∄m≡e ≡.refl (n & ())
%-- step : (e : Expression) → {_ : ∄ λ m → # m ≡ e} → Expression
%-- step (# m) {¬m} = ⊥-elim (¬m (m & ≡.refl))
%-- step (a ⊕ b) with ≡.inspect a
%-- step (a ⊕ b) | (_ ⊕ _) with-≡ ⊕≡a = step a {⊕≡e⇒∄m≡e ⊕≡a} ⊕ b
%-- step (a ⊕ b) | (# m) with-≡ m≡a with ≡.inspect b
%-- step (a ⊕ b) | (# m) with-≡ m≡a | (_ ⊕ _) with-≡ ⊕≡b = a ⊕ step b {⊕≡e⇒∄m≡e ⊕≡b}
%-- step (a ⊕ b) | (# m) with-≡ m≡a | (# n) with-≡ n≡b = # m + n
%\end{code}
%%endif
%%format step = "\func{step}"
%%format a′ = "a\Prime{}"
%%format b′ = "b\Prime{}"
%\begin{code}
%step : (e : Expression) → ℕ ⊎ Expression
%step (# m) = inj₁ m
%step (a ⊕ b) with step a
%step (a ⊕ b) | inj₁ m with step b
%step (a ⊕ b) | inj₁ m | inj₁ n = inj₂ (# (m + n))
%step (a ⊕ b) | inj₁ m | inj₂ b′ = inj₂ (a ⊕ b′)
%step (a ⊕ b) | inj₂ a′ = inj₂ (a′ ⊕ b)
%\end{code}
%%}}}%
%if False
\begin{code}
infix 3 _↦_ _↦⋆_ _↦⋆#_
\end{code}
%endif
The small-step semantics for |Expression|s is implemented in the same
fashion,
%format _↦_ = "\type{\anonymous{\mapsto}\anonymous}"
%format ↦ = "\type{{\mapsto}}"
%format _↦⋆_ = "\func{\anonymous{\mapsto}^\star\anonymous}"
%format ↦⋆ = "\func{{\mapsto}^\star}"
%format _↦⋆#_ = "\func{\anonymous{\mapsto}^\star\;" # "\anonymous}"
%format ↦⋆# = "\func{{\mapsto}^\star}\;" #
%format ↦-ℕ = "\cons{{\mapsto}\text-\mathbb{N}}"
%format ↦-L = "\cons{{\mapsto}\text-L}"
%format ↦-R = "\cons{{\mapsto}\text-R}"
\begin{code}
data _↦_ : Rel Expression Level.zero where
↦-ℕ : ∀ {m n} → # m ⊕ # n ↦ # (m + n)
↦-L : ∀ {a a′ b } → a ↦ a′ → a ⊕ b ↦ a′ ⊕ b
↦-R : ∀ {m b b′ } → b ↦ b′ → # m ⊕ b ↦ # m ⊕ b′
\end{code}
with \eqName{small-val}, \eqName{small-left} and \eqName{small-right}
represented as the |↦-ℕ|, |↦-L| and |↦-R| constructors. Using |Star| defined
earlier in this chapter, we obtain the reflexive transitive closure of
|_↦_|, along with proofs of the usual properties, for free:
\begin{code}
_↦⋆_ : Rel Expression Level.zero
_↦⋆_ = Star _↦_
\end{code}
%if False
\begin{code}
_↦⋆#_ : REL Expression ℕ Level.zero
e ↦⋆# m = e ↦⋆ # m
\end{code}
%endif
%}}}%
\subsection{Semantic Equivalence}%{{{%
%format denote→big = "\func{denote{\rightarrow}big}"
%format big→denote = "\func{big{\rightarrow}denote}"
%format a⇓m = "a{\Downarrow}m"
%format b⇓n = "b{\Downarrow}n"
Let us move swiftly on to the semantic equivalence theorems of section
\ref{sec:semantic-equivalence}. Our previous technique of rule induction
essentially boils down to induction on the structure of inductively-defined
data types.
The proof for the forward direction of theorem \ref{thm:denote-big}---which
captures the equivalence of the denotational and big-step semantics---is
implemented as |denote→big|, proceeding by case analysis on its argument |e
: Expression|:
\begin{code}
denote→big : ∀ {e m} → ⟦ e ⟧ ≡ m → e ⇓ m
denote→big {# n} ≡.refl = ⇓-ℕ
denote→big {a ⊕ b} ≡.refl = ⇓-⊕ (denote→big ≡.refl) (denote→big ≡.refl)
\end{code}
For the |e ≡ # n| case, matching the proof of |⟦ # n ⟧ ≡ m| with |≡.refl|
convinces the typechecker that |m| and |n| are equal, so |e ⇓ n| is
witnessed by |⇓-ℕ|. Agda considers terms equal up to $\beta$-reduction, so
the |⟦ a ⊕ b ⟧ ≡ m| argument is equivalently a proof of |⟦ a ⟧ + ⟦ b ⟧ ≡ m|,
by the definition of |⟦_⟧|. The goal type of |a ⊕ b ⇓ ⟦ a ⟧ + ⟦ b ⟧| is
therefore met with |⇓-⊕|, whose premises of |a ⇓ ⟦ a ⟧| and |b ⇓ ⟦ b ⟧| are
obtained by recursively invoking |denote→big| with two trivial witnesses to
|⟦ a ⟧ ≡ ⟦ a ⟧| and |⟦ b ⟧ ≡ ⟦ b ⟧|.
The backwards direction of theorem \ref{thm:denote-big} is given by the
following |big→denote|, which uses structural induction on the definition of
|_⇓_|:
\begin{code}
big→denote : ∀ {e m} → e ⇓ m → ⟦ e ⟧ ≡ m
big→denote ⇓-ℕ = ≡.refl
big→denote (⇓-⊕ a⇓m b⇓n) with big→denote a⇓m | big→denote b⇓n
big→denote (⇓-⊕ a⇓m b⇓n) | ≡.refl | ≡.refl = ≡.refl
\end{code}
For the base case of |⇓-ℕ|, it must follow that |e ≡ # m|, so the goal type
after $\beta$-reduction of |⟦_⟧| becomes |m ≡ m|, which is satisfied
trivially. The |⇓-⊕| inductive case brings with it two proofs of |a ⇓ m| and
|b ⇓ n|, which can be used to obtain proofs of |⟦ a ⟧ ≡ m| and |⟦ b ⟧ ≡ n|
via the induction hypothesis. The goal of |⟦ a ⟧ + ⟦ b ⟧ ≡ m + n| after
$\beta$-reduction is then trivially satisfied, thus completing the proof of
theorem \ref{thm:denote-big}.
The |with| keyword provides an analogue of Haskell's
$\keyw{case}\;\ldots\;\keyw{of}$ construct that allows us to pattern match
on intermediate results. Agda requires us to repeat the left hand side of
the function definition when using a |with|-clause, since dependent pattern
matching may affect the other arguments, as noted earlier in this chapter.
We go on to prove theorem \ref{thm:big-small}---the equivalence between the
big-step and small-step semantics---in a similar manner:
%format big→small = "\func{big{\rightarrow}small}"
%format a⇓m = "a{\Downarrow}m"
%format b⇓n = "b{\Downarrow}n"
\begin{code}
big→small : ∀ {e m} → e ⇓ m → e ↦⋆# m
big→small ⇓-ℕ = ε
big→small (⇓-⊕ {a} {b} {m} {n} a⇓m b⇓n) =
Star.gmap (λ a′ → a′ ⊕ b) ↦-L (big→small a⇓m) ◅◅
Star.gmap (λ b′ → # m ⊕ b′) ↦-R (big→small b⇓n) ◅◅
↦-ℕ ◅ ε
\end{code}
The goal in the case of |⇓-ℕ| is trivially satisfied by the empty reduction
sequence, since |e ≡ # m|. In the |⇓-⊕| case, recursively invoking
|big→small| with |a⇓m| and |b⇓n| gives reduction sequences for |a ↦⋆# m| and
|b ↦⋆# n| respectively. We can map over the former using |↦-L| over the
witnesses and |λ a′ → a′ ⊕ b| over the indices to obtain the reduction
sequence |a ⊕ b ↦⋆# m ⊕ b|, and likewise for the second to obtain |#
m ⊕ b ↦⋆ # m ⊕ # n|. By appending these two resulting sequences, followed by
a final application of the |↦-ℕ| rule---or equivalently invoking
transitivity---we obtain the desired goal of |a ⊕ b ↦⋆# m + n|.
%format small→big = "\func{small{\rightarrow}big}"
%format e′ = "e\Prime{}"
%format e↦e′ = "e{\mapsto}e\Prime{}"
%format e′↦⋆m = "e\Prime{}{\mapsto}^\star{}m"
%format sound = "\func{sound}"
%format a↦a′ = "a{\mapsto}a\Prime{}"
%format b↦b′ = "b{\mapsto}b\Prime{}"
%format a′⇓m = "a\Prime{\Downarrow}m"
%format b⇓n = "b{\Downarrow}n"
%format b′⇓n = "b\Prime{\Downarrow}n"
%format m⇓m = "m{\Downarrow}m"
The proof for the reverse direction of \ref{thm:big-small} proceeds by
`folding'\footnote{Unfortunately we cannot use |Star.gfold| from Agda's
standard library, as |_⇓_ : REL Expression ℕ| is not a homogeneous
relation.} lemma \ref{lem:small-sound}---implemented as the |sound| helper
function below---over the |e ↦⋆# m| reduction sequence:
\begin{code}
small→big : ∀ {e m} → e ↦⋆# m → e ⇓ m
small→big ε = ⇓-ℕ
small→big (e↦e′ ◅ e′↦⋆m) = sound e↦e′ (small→big e′↦⋆m) where
sound : ∀ {e e′ m} → e ↦ e′ → e′ ⇓ m → e ⇓ m
sound ↦-ℕ ⇓-ℕ = ⇓-⊕ ⇓-ℕ ⇓-ℕ
sound (↦-L a↦a′) (⇓-⊕ a′⇓m b⇓n) = ⇓-⊕ (sound a↦a′ a′⇓m) b⇓n
sound (↦-R b↦b′) (⇓-⊕ m⇓m b′⇓n) = ⇓-⊕ m⇓m (sound b↦b′ b′⇓n)
\end{code}
The |sound| helper performs case analysis on the structure of the |e ↦ e′|
small-step reduction. In the case of |↦-ℕ|, it must be the case that |e
≡ # m ⊕ # n| and |e′ ≡ # m + n| respectively, which in turn forces the |e′
⇓ m| argument to be |⇓-ℕ|. The goal type of |# m ⊕ # n ⇓ m + n| is
accordingly witnessed by |⇓-⊕ ⇓-ℕ ⇓-ℕ|.
For the two remaining |↦-L| and |↦-R| rules, it must necessarily be the case
that |e′ ≡ a′ ⊕ b| or |e′ ≡ # m ⊕ b′| respectively. Thus the |e′ ⇓ m|
argument contains a witness of either |a′ ⇓ m| or |b′ ⇓ n|, which we use to
recursively obtain a proof of |a ⇓ m| or |b ⇓ n|.
%}}}%
\subsection{Stack Machine, Compiler, and Correctness}%{{{%
%if False
\begin{code}
mutual
\end{code}
%endif
In section \ref{sec:stack-machine} we used a stack machine as our low-level
semantics, with the machine modelled as a pair of a list of instructions and
a stack of values. This translates to the following Agda data declaration:
%format Machine = "\type{Machine}"
%format ⟨_‚_⟩ = "\cons{\langle\anonymous{,}\anonymous\rangle}"
%format ⟨ = "\prefix{\cons{\langle}}"
%format ‚ = "\cons{,}"
%format ⟩ = "\postfix{\cons{\rangle}}"
%format σ = "\sigma"
%format σ′ = "\sigma\Prime{}"
%format Instruction = "\type{Instruction}"
%format PUSH = "\cons{PUSH}"
%format ADD = "\cons{ADD}"
\begin{code}
data Machine : Set where
⟨_‚_⟩ : (c : List Instruction) → (σ : List ℕ) → Machine
\end{code}
The |List| type from in Agda's standard library implements the familiar
\emph{nil} and \emph{cons} lists as found in Haskell. In turn, the virtual
machine's instruction set comprises the standard |PUSH| and |ADD| for stack
machines:
\begin{code}
data Instruction : Set where
PUSH : ℕ → Instruction
ADD : Instruction
\end{code}
%if False
\begin{code}
infix 3 _↣_
\end{code}
%endif
%format _↣_ = "\type{\anonymous{\rightarrowtail}\anonymous}"
%format ↣ = "\type{{\rightarrowtail}}"
%format _↣⋆_ = "\func{\anonymous{\rightarrowtail}^\star\anonymous}"
%format ↣⋆ = "\func{{\rightarrowtail}^\star}"
%format _↣⋆#_ = "\func{\anonymous{\rightarrowtail}^\star\texttt{\#}\anonymous}"
%format ↣⋆# = "\func{{\rightarrowtail}^\star\texttt{\#}}"
%format ↣-PUSH = "\cons{{\rightarrowtail}\text-PUSH}"
%format ↣-ADD = "\cons{{\rightarrowtail}\text-ADD}"
\noindent The two reduction rules \eqName{vm-push} \eqName{vm-add} for the
virtual machine are realised as the |↣-PUSH| and |↣-ADD| constructors of the
|_↣_| relation:
\begin{code}
data _↣_ : Rel Machine Level.zero where
↣-PUSH : ∀ {m c σ} → ⟨ PUSH m ∷ c ‚ σ ⟩ ↣ ⟨ c ‚ m ∷ σ ⟩
↣-ADD : ∀ {m n c σ} → ⟨ ADD ∷ c ‚ n ∷ m ∷ σ ⟩ ↣ ⟨ c ‚ m + n ∷ σ ⟩
_↣⋆_ : Rel Machine Level.zero
_↣⋆_ = Star _↣_
\end{code}
Again, we define |_↣⋆_| as |Star _↣_|, and receive the usual properties of
a reflexive transitive closure absolutely free.
%if False
\begin{code}
infix 3 _↣⋆_ _↣⋆#_
\end{code}
%endif
The compiler is defined identically to that of section \ref{sec:compiler},
%format compile = "\func{compile}"
\begin{code}
compile : Expression → List Instruction → List Instruction
compile (# m) c = PUSH m ∷ c
compile (a ⊕ b) c = compile a (compile b (ADD ∷ c))
\end{code}
which compiles a number |# m| to |PUSH m|, and a sum |a ⊕ b| to the
concatenation of code that computes the value of |a| and |b|, followed by an
|ADD| instruction.
The following definition of |_↣⋆#_| provides a convenient synonym for what
it means when we say that executing the compiled code for an expression |e|
computes the result |m|:
\begin{code}
_↣⋆#_ : REL Expression ℕ Level.zero
e ↣⋆# m = ∀ {c σ} → ⟨ compile e c ‚ σ ⟩ ↣⋆ ⟨ c ‚ m ∷ σ ⟩
\end{code}
Note that the above proposition is quantified over any code continuation |c|
and initial stack |σ|, and a proof of compiler correctness (theorem
\ref{thm:compiler-correct}) amounts to functions in both directions between
|e ⇓ m| and |e ↣⋆# m|. In the forward direction---that is, from the
high-level/big-step semantics to the low-level/virtual machine
semantics---this is implemented by induction on the structure of |e ⇓ m|:
%format big→machine = "\func{big{\rightarrow}machine}"
\begin{code}
big→machine : ∀ {e m} → e ⇓ m → e ↣⋆# m
big→machine ⇓-ℕ = ↣-PUSH ◅ ε
big→machine (⇓-⊕ a⇓m b⇓n) =
big→machine a⇓m ◅◅ big→machine b⇓n ◅◅ ↣-ADD ◅ ε
\end{code}
%format c_a
%format σ_a
%format c_b
%format σ_b
In the case of |⇓-ℕ| we have |e ≡ # m|, and so |↣-PUSH ◅ ε| witnesses the
reduction sequence |∀ {c σ} → ⟨ compile (# m) c ‚ σ ⟩ ↣⋆ ⟨ c ‚ m ∷ σ ⟩|. In
the second case, the recursive terms |big→machine a⇓m| and |big→machine b⇓n|
are of the following types:
\begin{spec}
big→machine a⇓m : ∀ {c_a σ_a} → ⟨ compile a c_a ‚ σ_a ⟩ ↣⋆ ⟨ c_a ‚ m ∷ σ_a ⟩
big→machine b⇓n : ∀ {c_b σ_b} → ⟨ compile b c_b ‚ σ_b ⟩ ↣⋆ ⟨ c_b ‚ n ∷ σ_b ⟩
\end{spec}
The right hand side requires a proof of:
\begin{gather*}
|∀ {c σ} → ⟨ compile (a ⊕ b) c ‚ σ ⟩ ↣⋆ ⟨ c ‚ m + n ∷ σ ⟩|
\end{gather*}
which can be obtained by instantiating |c_a = compile b (ADD ∷ c)|, |c_b
= ADD ∷ c|, |σ_a = σ|, |σ_b = m ∷ σ|, and concatenating the resulting
reduction sequences, followed by a final application of the |↣-ADD| rule. As
these values can be automatically inferred by Agda, we do not need to make
them explicit in the definition of |big→machine|.
%{{{%
%if False
\begin{spec}
small→machine : ∀ {e m} → e ↦⋆# m → e ↣⋆# m
small→machine ε = ↣-PUSH ◅ ε
small→machine (↦-ℕ ◅ ε) = ↣-PUSH ◅ ↣-PUSH ◅ ↣-ADD ◅ ε
small→machine (↦-ℕ ◅ () ◅ xs)
small→machine (↦-L a↦a′ ◅ a′⊕b↦m) = {!small→machine a′⊕b↦m!}
small→machine (↦-R b↦b′ ◅ na⊕b′↦m) = {!!}
\end{spec}
%endif
%}}}%
The backwards direction of the compiler correctness proof requires us to
compute a witness of |e ⇓ m| from one of |e ↣⋆# m|. We first need to
implement a pair of lemmas, however.
%format exec = "\func{exec}"
%format na = "n_a"
%format nb = "n_b"
%format a↣⋆#na = a "{\rightarrowtail^\star}\texttt{\#}" na
%format b↣⋆#nb = b "{\rightarrowtail^\star}\texttt{\#}" nb
The |exec| lemma simply states that that for any expression |e|, there
exists a number |m| for which executing |compile e| computes |m|:
\begin{code}
exec : ∀ e → ∃ λ m → e ↣⋆# m
exec (# m) = m ∧ λ {c} {σ} → ↣-PUSH ◅ ε
exec (a ⊕ b) with exec a | exec b
exec (a ⊕ b) | na ∧ a↣⋆#na | nb ∧ b↣⋆#nb = na + nb ∧
λ {c} {σ} → a↣⋆#na ◅◅ b↣⋆#nb ◅◅ ↣-ADD ◅ ε
\end{code}
When |e ≡ # m|, this number clearly has to be |m|, with |↣-PUSH ◅ ε| serving
as the witness. Note that due to Agda's handling of implicit arguments, we
must explicitly generalise over |c| and |σ|. In the |e ≡ a ⊕ b| case, we
simply recurse on the |a| and |b| subexpressions and concatenate the
resulting reduction sequences with an |↣-ADD|.
%format unique = "\func{unique}"
%format σ′ = σ "\Prime{}"
%format σ″ = σ "\PPrime{}"
%format σ₀ = σ "_0"
%format c′ = c "\Prime{}"