-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathlestradespecification.sml
5710 lines (3411 loc) · 180 KB
/
lestradespecification.sml
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
(*
\documentclass[12pt]{article}
\usepackage{amssymb}
\title{My Own Private Automath: the Lestrade dependent typed system and the specification code for its implementation, the Lestrade Type Inspector}
\author{M. Randall Holmes}
\date{4/13/2020 -- This is a beta version of the second release, without rewriting. It runs the entire Zermelo implementation. It also ran the Automath translations without any changes.}
\begin{document}
\maketitle
\tableofcontents
\newpage
\subsection{Version Notes}
\begin{description}
\item[4/14/2020:] Improved Showmoves command. Refined save command so that when it overwrites a saved move
it will eliminate extensions of that saved move. These things are motivated by rewriting a Lestrade manual for the previous version to accord with this one.
\item[4/13/2020:] Some tweaks to unsubs functions which may or may not be needed. I was having a problem in a legacy file which I thought was a problem with the implicit argument mechanism, but it wasn't: it was actually an issue with the order in which implicit arguments were declared. The old version puts implicit arguments in a different order; this version puts them in the literal order in which they are declared, and this can turn out to be an issue! A change in declaration order and the offending file worked correctly.
\item[4/12/2020:] I have realized why there are so few let terms even after minimizing obvious expansions: there is heavy expansion going on in arguments, which I might be able to suppress. Actually, it doesn't seem so easy. The idea for preserving more let terms would be to allow object arguments to have let binders.
Fixed error which allowed colons \verb : to be special characters.
\item[4/10/2020:] No code update. I should attend to updating all the existing text not using rewrites for testing and demonstration purposes.
\item[4/9/2020:] Fixed a bug in the refinement of equalfunctions. Added a toggle {\tt Zeroorone}
which revises typesimplify so that it reduces let terms with one (the default) or no occurrences of the bound variable (when strong expansion is off).
\item[4/8/2020:] Put in a shortcut for computation of equality between functions with let term arguments.
It may in theory cause false negatives, but it doesn't seem likely; if this does turn out to be a danger,
this step could be toggled on or off? Basically, if two functions being compared have binders starting with
the same let term, drop the let term (changing names of the bound object to bring the names into line in the
two terms).
\item[4/7/2020:] Updating betared and typered without expansion of let terms.
The fix to make betared and typered not energetically expand let terms makes the function recursive again...
I am beginning to think that, while the let term enhancement is philosophically valuable, the strong expansion approach is the practical one. I should still look
at trying to fix equality and matching to be less energetic about expanding let terms.
\item[4/6/2020:] Starting a read and comment pass.
I added the {\tt test} command, which here displays a function and so in fact any kind of term. I added application of {\tt typesimplify} to function sorts
if strong expansion is not on.
Test beta reduction without expansion of let terms.
Operator precedence?
Figuring out how to remove {\tt def} (and the forced return) from lambda terms would be a good thing.
Closing a move with the default name should eliminate all non-default extensions of that move. The treatment of saved moves should be tested; there remains the one
spot of local bad behavior that I had to correct in the Zermelo implementation.
It might be a good idea to eliminate type information about arguments where possible: testing for the presence of free occurrences of bound variables would seem to
be an adequate test.
A low priority issue: with a little cleverness we could automatically display functions as curried application terms to whatever extent is possible.
A useful feature to add would be save files which simply record and restore the declarations.
Typesimplify should be run on types independently.
One thing to note is that in the strong expansion version, we can safely discard all type information about arguments, because it is always deducible from the body of the argument.
This does not work in versions with visible let terms. This may have some effect in performance, since for example substitutions have to be made in all those types.
There is the continuous extreme slowness when building large terms, which the original also has. One wonders whether there is something one could cache which would solve this problem.
\item[4/5/2020:] I seem to have my original let term simplification protocol working. I should probably
set up the weak version to work as well. There is quite a lot of tweaking to be done now that let terms
can actually be seen in use.
For example, redundant let terms should be eliminated from types of defined notions.
We have unequivocal success of a version with first class let terms, but it is clear that they are not being used optimally.
\item[4/3/2020:] I think I have repaired the problem with the approach without strong expansion,
which had to do with problems that arose from discarding type information in typesimplify which turns
out to be needed for computing function sorts in the let term approach.
Now the monstrous inefficiencies unsuspected of this approach need to be investigated.
\item[4/2/2020:] I identified the silly bug which was causing infinite loops without strong expansion. It probably
could have happened with strong exppansion, too; my good fortune.
The bug in the Zermelo interpretation without strong expansion is a failure of implicit argument inference; I'm on it.
\item[4/1/2020:] This version runs the Zermelo implementation. I believe it is now a full implementation of Lestrade without rewrites, roughly
as reliable as the first version, and with much cleaner code, easier to maintain.
For the record, I note that the crucial update for fixing the slowness was caching the dependency list functions.
I want to debug it to the point where the pure approach with minimal type simplification works (so we can really
see let terms in use).
Of course rewrites remain to be fixed.
I am going to remove most of the daily notes.
Apart from actual bugs, the issues encountered which required changes in the Zermelo implementation files were parser issues (spaces needed between special character infixes asd following numerals; the final argument of a prefix term cannot be an infix argument) which I want to leave as they are (the new version is better) and some issues with
correct handling of saved moved which forced me to change some identifier names at one point in the development. I should check that my environment saving actually works as intended.
The further issue is that this version suppresses unfixed problems with let terms by expanding everything out much as the original implementation did. It ought to be possible to fix this so that the prover also works when the strong expansion toggle is turned off (and it would be interesting to see what happens once this is done).
Let terms {\em are\/} used in this version, but invisibly.
An essay on the rather different bound variable management in this version would be a good idea.
\item[3/30/2020:] Working on the slowness problem. Made it do head substitution for laughs.
Tweaked the equalobjects algorithm again, making it always prefer to adjust the second item unless
ages of definitions dictate otherwise. I still may prefer to adjust the first item by preference if
the adjustment of the second is deep.
\item[3/29/2020:] Installed the ability to toggle strong expansion ({\tt Strongexpand} command). The version with minimal
type simplification (just dropping unused let terms) crashes for reasons I do not understand which should be
fixed, though it mostly works.
Commented diagnostic messages out so that arguments are not silently being built.
\item[3/24/2020:]
Enabled compressed display; an explicit call to Showdec will always give the full display.
\item[3/14/2020:]
As my last move, I got the new Lestrade to run the entire third file by writing typesimplify in a way
which entirely or almost eliminates let terms. This shows that the underlying logic is implemented, and things
can probably be tweaked so that we can take advantage of the let term machinery.
Let terms as part of the logic are of course charming because they make binders of lambda terms
exactly like moves.
I'm preserving this old note because it bears on current business.
\item[3/11/2020 status report:]
There are minor differences between the parsers in the two versions. I am inclined to leave the current parser as it is: the places in which it differs have merit. The final argument of a prefix term will not be an infix argument (parenthesize it if there is an issue). The final argument of a prefix term must be set off
from the previous argument by a comma if it is mixfix; the previous version detects that there is no danger in this case, but it is probably better to leave this version as it is.
\item[3/10/2020:]
Interesting innovation in pretty printing: put breaks before \verb ({def} and \verb ({let} . This further suggests that we should not put the def into lambda terms appearing as arguments.
I found a subtle parser difference between the old version and the new which I think I will leave as is
(and make corrections when it arises in files). There is already a difference involving the need
to put spaces between infixes and numerals.
\item[3/7/2020 status report:] This should be a fairly capable implementation of the
Lestrade language and declaration system before rewrite rules.
Its language and its command format differ slightly from those of version 1.0, but
version 2.0 should accept any version 1.0 declaration command line
(with the exception of rewrite commands). Its file format is
totally different, and I need to enable version 1.0 to export files for version 2.0 to read.
Some highlights:
\begin{enumerate}
\item Let arguments in functions are a profound change in structure. I'm not sure what
the performance issues are until I am able to run large files to test. They create an exact
parallelism between binder lists in functions and moves as data structures which did not quite exist before.
\item I have plans to put the rewrite system on a firmer foundation by introducing rewrite objects
which will similarly appear as arguments.
\item The old version required the user to enter arguments in the order in which they are declared in the next move. This version automatically (and silently) reorders them.
\item I {\em think\/} that the implicit argument mechanism might be a little more general. The development
of matching was more principled, and this may pay off because the same matching functions may be usable for
development of rewriting.
\item The format of console output is tidier (though handling of prompts makes console output
a bit uglier than it needs to be when running a file; probably fixable but not important).
\item The type of Lestrade entities is much more compact and the fact that all entities can
be type cast to functions is both striking and very useful.
\item The format of files is entirely different, and I think better and easier to handle. I need to write export function so that version 1.0 can produce version 2.0 files. I'm frightened of what will happen, though!
\end{enumerate}
\end{description}
\newpage
\section{Introduction}
This document is intended to serve as a specification of the Lestrade dependent type system and to contain code for the implementation (the Lestrade Type Inspector) in parallel with the specification. The document
should be easily converted between LaTeX code and Standard ML code. The code is for Moscow ML 2.01; I should add the preamble(s) needed to run it in other versions of ML.
The following code contains list handling procedures and handles input and output file declarations and various flavors of messages from Lestrade. Notice that everything
that Lestrade says without exception goes to the output file as well as the screen.
I am thinking of straining the paradigm of literate programming by in effect making this the long version of the paper on Lestrade, too.
In another iteration of literate programming, I now have the ability to embed Lestrade commands in this text and have the Lestrade file reader process those sections
and insert the correct dialogue.
This version has met a benchmark: it now runs the first four files in my Zermelo implementation with some minimal changes having to do with slight differences in the parser,
and in one place a need to correct for some unexplained difficulty with move saving.
\newpage
\section{Philosophical Introduction}
Lestrade is a system of dependent type theory. It is implemented by the Lestrade Type Inspector, an environment for developing and checking systems of Lestrade declarations, which is in effect a theorem prover of the Automath family. Since it is also in effect a type checker for a type system more than adequate for a language like ML, this suggests that the Inspector might reasonably be extended to an interpreter for a strongly typed programming language, with the additional merit that it could handle types for general mathematical objects and proofs.
There is prior art. Lestrade is motivated by prior familiarity with Automath. Automath has other descendants, notably Coq and Lean (to name systems that are often mentioned). Lestrade is distinguished by a minimalist approach: one way of putting this is that Lestrade was designed as an implementation of an approach to the philosophy of mathematics, and with this in mind is very streamlined. Lestrade is also formally weaker than Automath for principled reasons (though the full strength of any Automath theory can be obtained by suitable declarations). We believe that it is also the case that the core logic of any Automath descendant can be implemented in Lestrade, but this requires some verification. It can be noted that Lestrade has a particular advantage over Automath in the area of flexible local use of names, which is only improved in this new version.
We indicate our philosophical motivations, though these may be better explored implicitly by seeing what the system can do.
We are inclined to view functions as determined by expressions with holes in them (finite objects) rather than infinite tables of values. This does not mean that we identify
functions with pieces of text\footnote{For example, the identity conditions of functions are different from those of associated text: renaming of bound variables illustrates this.}. Looking at the status of the ``holes" suggests that we should be friendly to the idea of variables as representing ``arbitrary entities" [though this is known to be perilous]. In a sense to be made concrete in the description of the system, we view a variable or parameter of a definition as an object in a ``possible world" (the ``next move": the ``worlds" are actually called ``moves" in Lestrade parlance). The declaration of a function in Lestrade involves defining an expression for an entity in the ``next move" (an expression with the same tenuous metaphysical status
as the parameters living in it); one then postulates a function implementing this, an entity in the ``last move", one step less tenuous: the variables free in the original expression for the variable-dependent object declared in the next move are bound in the expression for the function declared in the last move. This may sound mysterious, but it is concretely implemented in the Lestrade declaration commands. Our underlying philosophy is Aristotelean: we want to appeal to potential rather than actual infinities.
We view proofs as a species of mathematical object. This is implemented using the Curry-Howard isomorphism: for example, a proof of the implication $p \rightarrow q$ is associated with a function from proofs of $p$ to proofs of $q$. This means that each proposition has to have a correlated sort of proofs of (we prefer to say evidence for) that proposition. This in turn commits us to type theory, and in fact dependent type theory (as we will see when we look at examples).
It should be noted that the ingredients we have mentioned so far might imply that we have a constructive viewpoint. In fact, we do not. We are charmed by the apparent fact that classical
Cantorian mathematics is perfectly well accommodated by the framework we develop. Lestrade also implements a constructive viewpoint easily: Lestrade has very few primitive notions, and the user has a great deal of freedom in setting up the basics of her logic. It can be noted that the original Automath system, development of which was the context of one of the independent inventions of the Curry-Howard isomorphism, also was used primarily to implement theories with classical logic, though its descendant Coq now encourages a constructive approach.
We also note that our major implementation project is an implementation of Zermelo's 1908 proof of the Well-Ordering Theorem from the Axiom of Choice, which can hardly be viewed a constructive result!
Further, it appears that our philosophical prejudices strongly suggest a project of computer implementation of mathematics. And this is what we proceed to carry out.
Fog in our remarks above can be dispelled by concrete discussion of what is to be implemented in code below, examination of the code when it exists, and use of the software to implement
mathematical proofs. We set about an explicit description of what we are up to, which will probably bring in more philosophical points.
Expressions in the language of Lestrade can represent {\em sorts} (types in the internal sense of Lestrade) and {\em entities\/}, which have sorts. Entities are further partitioned
into {\em objects\/} and {\em functions\/}. It should be noted that objects are metaphysically primary for us; for a set theorist, a nice way of putting this is that Lestrade functions
are best thought of as proper class functions\footnote{with the odd point that they can take proper class functions as input.} (functions which are sets will be objects generated from proper class functions by suitable constructors: this can be seen in actual theories).
The metaphysical primacy of objects over functions is also seen in our implementation of proofs as objects. The proof of an implication $p \rightarrow q$ is not identified with
the associated function from proofs of $p$ to proofs of $q$: proofs are objects, not functions. We provide a constructor sending functions witnessing an implication to proofs of the implication. This can be seen in examples which will be presented.
We describe the primitive system of object sorts.
\begin{enumerate}
\item {\tt prop} is an object sort, the sort of propositions.
\item {\tt type} is an object sort, the sort of labels for sorts of mathematical object. We think of {\tt type} as inhabited by type {\em labels\/} because we resist the idea that a sort is a set: a sort is a (finite) feature of a particular object encountered, not the infinite collection of all objects with that feature, in our Aristotelean philosophical viewpoint.
\item {\tt obj} is a sort provided for mathematical objects thought of as ``untyped". The sets in an implementation of ZFC would probably be of this sort.
\item {\tt that} $p$, where $p$ is a term of sort {\tt prop}, is a sort, inhabited by proofs of (or evidence for) the proposition $p$.
\item {\tt in} $\tau$, where $\tau$ is a term of sort {\tt type}, is a sort, inhabited by objects of the type with label $\tau$. To make it clearer what we are up to, if {\tt Nat} were the type of natural numbers, {\tt Nat} would be of sort {\tt type} and individual natural numbers would be of sort {\tt in Nat}.
\end{enumerate}
Each Lestrade function takes a fixed positive number of arguments. The sort of each of its arguments is fixed by the sort of the function, in a way which may depend
on earlier arguments. The output of the function must be of an object sort fixed by the sort of the function. The fact that we require that function outputs be objects is related to
our views on the metaphysical primacy of objects, and also encouraged by features of the Lestrade declaration environment. It can be noted that functions supplied with short
argument lists are read as functions (by a version of the well-known device of currying) and in this way a Lestrade function may be read as having function output in some contexts.
In the context of this philosophical introduction, this might best be communicated by giving some examples. A more formal description appears below in the comments on the code.
Logical conjunction is a function taking two arguments of type {\tt prop} to a proposition, so its type is $[(p:{\tt prop}),(q:{\tt prop}) \Rightarrow {\tt prop}]$. Decorating arguments with variable names might seem excessive, but the reasons will become clear.
The rule of conjunction takes a proof $pp$ of $p$ and a proof $qq$ of $q$ to a proof of $p \wedge q$. The type of this is
$$[(p:\verb prop ),(q:{\tt prop}),(pp: \verb that \, p),(qq:{\tt that}\, q) \Rightarrow {\tt that}\, p \wedge q].$$
One might think that the rule of conjunction has only two arguments, the proofs of the two components. But it also needs to know what the two propositions are. On the other hand, having to enter the first two arguments and display them seems excessive, since they can be inferred from the last two. The intention is that Lestrade will type the rule of conjunction as shown but allow the suppression of the first two arguments in input and output (the feature of implicit argument inference). This illustrates what is meant by the assertion that the
sorts of arguments may depend on earlier arguments.
If $pp$ is a proof of $p$ and {\tt Conj} is the rule of conjunction, then ${\tt Conj}(p,p,pp,pp)$ is a proof of $p \wedge p$ (which we intend to be able to enter as ${\tt Conj}(pp,pp)$ when we have full support for implicit argument inference).
We exhibit a capability of the Lestrade Type Inspector.
\begin{verbatim}
>>> declare p prop
>>> declare q prop
>>> postulate & p q prop
>>> declare pp that p
>>> declare qq that q
>>> postulate Conj p q pp qq that p & q
>>> define test pp : Conj(p,p,pp,pp)
\end{verbatim}
Above are some Lestrade commands. If we enter these commands bracketed by {\tt begin Lestrade execution} and {\tt end Lestrade execution}, the Lestrade Type Inspector can process the commands in this very file and insert the dialogue, as below:
\begin{verbatim}
begin Lestrade execution
>>> declare p prop
p : prop
{move 1}
>>> declare q prop
q : prop
{move 1}
>>> postulate & p q prop
&: [(p_1 : prop), (q_1 : prop) =>
(--- : prop)]
{move 0}
>>> declare pp that p
pp : that p
{move 1}
>>> declare qq that q
qq : that q
{move 1}
>>> postulate Conj p q pp qq that p & q
Conj : [(p_1 : prop), (q_1 : prop), (pp_1
: that p_1), (qq_1 : that q_1) =>
(--- : that p_1 & q_1)]
{move 0}
>>> define test pp : Conj (p, p, pp, pp)
test : [(.p_1 : prop), (pp_1 : that
.p_1) => ({def} Conj (.p_1, .p_1, pp_1, pp_1) : that
.p_1 & .p_1)]
test : [(.p_1 : prop), (pp_1 : that
.p_1) => (--- : that .p_1 & .p_1)]
{move 0}
end Lestrade execution
\end{verbatim}
The reader may get the idea that we have presented the actual implementation of the discussion of conjunction above.
It seems the moment to talk about the Lestrade declaration environment. This is a list of lists of declarations called ``moves".
Each declaration introduces an identifier with a hidden numerical parameter indicating the move in which it is declared. Further it gives either a function sort for the entity represented by the identifier (this may be a 0-ary function sort, in which case it is construed as an object) if the entity is a primitive notion, axiom, or primitive rule of inference, or a lambda-term defining the entity precisely (which will be 0-ary if the defined entity is an object). The second case is that of defined entities, theorems, and derived rules of inference. The original commands of Lestrade unpack the function sort or lambda term into a list of parameters and an object sort or specific object output.
There are always at least two moves. Move 0 contains unequivocally declared objects, functions, axioms, theorems and rules of inference of the theory under construction.
The highest-indexed move (called the ``next move") contains entities which are completely hypothetical in character, used as parameters in definitions and bound variables
in declared lambda-terms and function sorts. The entities in the second-to-last move (the ``last move" or the ``current move") are entities which are for the moment definitely
posited, but this is relative in the following sense: the {\tt close} command will discard all declarations in the next move\footnote{This is where we gain the benefit of flexible local use and re-use of names.}, whereupon the declarations in the new next move (the erstwhile last move) will acquire the tenuous character they had when originally introduced; on the other hand, the {\tt open} command will introduce a new empty ``next move" and allow us to posit all entities
in the erstwhile next move more definitely$\ldots$ for the moment.
Three styles of declaration command occur in the example above. The {\tt declare} command declares an identifier which is intended for use as a parameter.
The {\tt postulate} command introduces an identifier (with a list of arguments already declared in the next move) and an object sort, and declares the identifier
as a primitive construction (in the last move, not the next move) taking arguments of the indicated sorts to an object of the given sort. Since declarations in the next move can depend on objects appearing earlier in the next move, this supports declarations of functions with dependent types. The {\tt define} command takes an identifier, a list of arguments taken from the next move, and an object term, and declares the identifier as the function in the last move with the given output (defining a lambda-term in effect). There are natural requirements
that all dependencies of the items in one of these declaration commands on things declared in the next move are actually present in the argument list (or, eventually, may be inferred by the implicit argument inference feature).
When function sort and lambda-term expressions {\em are\/} entered by the user, bound variables will always be copies of variables declared in the next move. This has the practical effect
that it is never necessary to embed types of bound variables in terms as entered by the user, and in fact this is not supported by the parser.
It is a possibly interesting feature of the logic of Lestrade that one can in principle realize any dependent type using just the {\tt open}, {\tt close}, {\tt declare}, {\tt postulate}, and {\tt define} commands, with the user writing no terms other than atomic object and function terms and object application terms (this has the specific effect that one can only {\tt declare} object identifiers). It is not in principle necessary for the user ever to write
a term in which variables are bound, whether it is a function sort term or a lambda-term. This was not true in Automath: the version PAL of Automath in which terms with variable binding were not written by the user was of interest but was strictly weaker than Automath. The reason that things are different here is the more complicated declaration environment: the clever use of moves allows one to avoid variable binding (though one does not want to do this in practice). The first version of Lestrade actually enforced this restriction. We overstated the extent to which this original version of Lestrade ``did not have bound variables" : the appearance of function sorts and lambda terms in output was unavoidable. It also turned out to be extremely valuable in practice to be able to {\tt declare} identifiers of function sorts and to supply lambda-terms as arguments to functions. The language of Lestrade continues (in an analogy to the language of Russell and Whitehead in {\em Principia Mathematica\/}) not to support lambda-terms in applied position: substitution of a lambda-term for an applied function variable always induces immediate beta-reduction.
The {\tt close} command discards the declarations made in the next move. It is however possible to save the next move beforehand (usually with an explicit name), which allows
a set of local declarations of interest to be re-opened for later use. This has the side-effect that masking of declarations is possible. It is never possible to declare an identifier
in the next move or the last move which conflicts with an identifier already introduced in either of those moves or any earlier move, but it is possible to save a move, close it, introduce identifiers in earlier moves which conflict with those in the saved move, and reopen the saved move. In this case, the identifiers of the saved move will mask the ones introduced later but at earlier moves, but declarations depending on the masked identifiers will work correctly (the current implementation handles this more neatly than the original implementation).
It is a further technicality (mostly important in move 1, where the {\tt close} command will not work) that one may clear the next move without exiting it, using the {\tt clearcurrent} command, and one may use the {\tt clearcurrent} command as well as the {\tt open} command with a name as argument to open a saved set of declarations as the next move.
As we have intimated above, our idea is that the moves are to be thought of as possible worlds. A modality which might be useful as a metaphor here is temporal: think of an entity declared in the next move as an entity of the appropriate type which is unknown to the user, and so can play the role of any entity of that type she may encounter in the future.
In the current implementation, the role of the entities declared in the next move is primarily as bound variables. The original {\tt declare}, {\tt postulate}, and {\tt define}
commands are implemented in terms of new {\tt Declare} and {\tt Posit} commands. Each of these commands takes a function sort (for a primitive notion) or a lambda-term (for a defined notion) as its argument: {\tt Declare} makes declarations to the next move and {\tt Posit} to the last move. The other commands are implemented by using the argument
lists as binders along with the final argument as body to construct a function sort or lambda-term. But the legacy commands remain available to the user, and the fact that these parameter based commands can implement anything normally implemented with variable binding remans interesting.
We introduce an extended example in the old style which does not support user-entered function sorts and lambda-terms.
\begin{verbatim}
begin Lestrade execution
>>> Clearall
{move 1}
>>> open
{move 2}
>>> declare x obj
x : obj
{move 2}
>>> postulate P x : prop
P : [(x_1 : obj) => (--- : prop)]
{move 1}
>>> close
{move 1}
>>> comment The effect of the little block \
above is to declare P as a function (actually \
predicate) parameter .Being postulated \
when move 2 is the next move then closing \
leaves P as a predicate of untyped objects \
(obj) at move 1, a predicate parameter \
.
{move 1}
>>> postulate set P : obj
set : [(P_1 : [(x_2 : obj) => (---
: prop)]) => (--- : obj)]
{move 0}
>>> comment Above we declare set builder \
notation : set P can be read {x : P (x)} .
{move 1}
>>> declare x obj
x : obj
{move 1}
>>> declare y obj
y : obj
{move 1}
>>> postulate E x y : prop
E : [(x_1 : obj), (y_1 : obj) =>
(--- : prop)]
{move 0}
>>> comment We declare the membership \
relation .
{move 1}
>>> declare x1 that P x
x1 : that P (x)
{move 1}
>>> postulate comp P, x x1 : that E x set \
P
comp : [(P_1 : [(x_2 : obj) => (---
: prop)]), (x_1 : obj), (x1_1
: that P_1 (x_1)) => (--- : that
x_1 E set (P_1))]
{move 0}
>>> declare x2 that E x set P
x2 : that x E set (P)
{move 1}
>>> postulate comp2 P, x x2 : that P x
comp2 : [(P_1 : [(x_2 : obj) => (---
: prop)]), (x_1 : obj), (x2_1
: that x_1 E set (P_1)) => (---
: that P_1 (x_1))]
{move 0}
>>> declare p prop
p : prop
{move 1}
>>> comment We declare the unrestricted \
axiom of set comprehension (sending evidence \
for P (x) to evidence for {x : P (x)} and \
vice versa) .Of course this assumption \
will prove disastrous .
{move 1}
>>> declare q prop
q : prop
{move 1}
>>> postulate Implies p q : prop
Implies : [(p_1 : prop), (q_1 : prop) =>
(--- : prop)]
{move 0}
>>> postulate False : prop
False : prop
{move 0}
>>> declare pp that p
pp : that p
{move 1}
>>> declare rr that Implies p q
rr : that p Implies q
{move 1}
>>> postulate Mp p q pp rr : that q
Mp : [(p_1 : prop), (q_1 : prop), (pp_1
: that p_1), (rr_1 : that p_1 Implies
q_1) => (--- : that q_1)]
{move 0}
>>> declare absurd that False
absurd : that False
{move 1}
>>> postulate Panic p absurd : that p
Panic : [(p_1 : prop), (absurd_1
: that False) => (--- : that p_1)]
{move 0}
>>> define Not p : Implies p False
Not : [(p_1 : prop) => ({def} p_1
Implies False : prop)]
Not : [(p_1 : prop) => (--- : prop)]
{move 0}
>>> open
{move 2}
>>> declare pp2 that p
pp2 : that p
{move 2}
>>> postulate Ded pp2 : that q
Ded : [(pp2_1 : that p) => (---
: that q)]
{move 1}
>>> close
{move 1}
>>> postulate Impliesproof p q Ded : that \
Implies p q
Impliesproof : [(p_1 : prop), (q_1
: prop), (Ded_1 : [(pp2_2 : that
p_1) => (--- : that q_1)]) =>
(--- : that p_1 Implies q_1)]
{move 0}
>>> comment Above find the needed primitives \
of propositional logic .
{move 1}
>>> define Russell x : Not E x x
Russell : [(x_1 : obj) => ({def} Not
(x_1 E x_1) : prop)]
Russell : [(x_1 : obj) => (--- : prop)]
{move 0}
>>> define R : set Russell
R : [({def} set (Russell) : obj)]
R : obj
{move 0}
>>> open
{move 2}
>>> comment We define the Russell predicate \
of non - self - membership and the \
associated set .
{move 2}
>>> declare R1 that E set Russell, set \
Russell
R1 : that set (Russell) E set (Russell)
{move 2}
>>> define R2 R1 : comp2 Russell, set \
Russell, R1
R2 : [(R1_1 : that set (Russell) E set
(Russell)) => ({def} comp2
(Russell, set (Russell), R1_1) : that
Russell (set (Russell)))]
R2 : [(R1_1 : that set (Russell) E set
(Russell)) => (--- : that Russell
(set (Russell)))]
{move 1}
>>> define R3 R1 : Mp E set Russell, set \
Russell, False R1 R2 R1
R3 : [(R1_1 : that set (Russell) E set
(Russell)) => ({def} Mp (set
(Russell) E set (Russell), False, R1_1, R2
(R1_1)) : that False)]
R3 : [(R1_1 : that set (Russell) E set
(Russell)) => (--- : that False)]
{move 1}
>>> close
{move 1}
end Lestrade execution
\end{verbatim}
Here we insert a comment in ordinary text (by interrupting the Lestrade execution block): R4 takes definition arguments,
which we originally had to supply in this version, but which
are now supplied implicitly.
\begin{verbatim}
begin Lestrade execution
>>> define R4 : Impliesproof E set Russell, set \
Russell, False R3
R4 : [({let} .R2_1 : [(R1_2 : that
set (Russell) E set (Russell)) =>
({def} comp2 (Russell, set
(Russell), R1_2) : that Russell
(set (Russell)))]), ({let} .R3_1
: [(R1_2 : that set (Russell) E set
(Russell)) => ({def} Mp (set
(Russell) E set (Russell), False, R1_2, .R2_1
(R1_2)) : that False)]) =>
({def} Impliesproof (set (Russell) E set
(Russell), False, .R3_1) : that
(set (Russell) E set (Russell)) Implies
False)]
R4 : [({let} .R2_1 : [(R1_2 : that
set (Russell) E set (Russell)) =>
({def} comp2 (Russell, set
(Russell), R1_2) : that Russell
(set (Russell)))]), ({let} .R3_1
: [(R1_2 : that set (Russell) E set
(Russell)) => ({def} Mp (set
(Russell) E set (Russell), False, R1_2, .R2_1
(R1_2)) : that False)]) =>
(--- : that (set (Russell) E set
(Russell)) Implies False)]
{move 0}
>>> define R5 : comp Russell, set Russell, R4
R5 : [({def} comp (Russell, set
(Russell), R4) : that set (Russell) E set
(Russell))]
R5 : that set (Russell) E set (Russell)
{move 0}
>>> define R6 : Mp E set Russell, set \
Russell, False R5 R4
R6 : [({def} Mp (set (Russell) E set
(Russell), False, R5, R4) : that
False)]
R6 : that False
{move 0}
>>> comment and the familiar proof of \
absurdity ensues .
{move 1}
end Lestrade execution
\end{verbatim}
\newpage
Now we begin the actual code of the Type Inspector with some utilities.
\begin{verbatim}
*)
(* utility code *)
open TextIO;
open OS;
fun fileexists s = OS.FileSys.access (s, []);
val LOGFILE = ref(openOut("default"));
val READFILE = ref(openIn("default2"));
fun say s = (output(stdOut,"\n"^s);
output (stdOut,"\n\n(paused, type something to continue) >");
flushOut(stdOut);
output(!LOGFILE,"\n Inspector Lestrade says: "^s);
output (!LOGFILE,"\n\n(paused, type something to continue) >");
flushOut(!LOGFILE);
input(stdIn);());
fun say2 s = (output(stdOut,"\n"^s);
output (stdOut,"\n >>"); flushOut(stdOut);
output(!LOGFILE,"\n"^s);
(* output (!LOGFILE,"\n >>"); *) flushOut(!LOGFILE);
(* input(stdIn); *)());
fun say3 s = (output(stdOut,"\n"^s);
output (stdOut,"\n"); flushOut(stdOut);
output(!LOGFILE,"\n"^s);
output (!LOGFILE,"\n"); flushOut(!LOGFILE);
(* input(stdIn); *)());
val DIAGNOSTIC = ref false;
fun diagnostic() = DIAGNOSTIC := not(!DIAGNOSTIC);
fun sayD s = if (!DIAGNOSTIC) then say3 s else (); (* diagnostic messages *)
fun say2D s = if (!DIAGNOSTIC) then say2 s else (); (* diagnostic messages *)
fun versiondate() = say3
("Welcome to the Lestrade Type Inspector\n"^
"version 2.0, release of 4/13/2020\n4:30 pm Boise time.")
fun explain b s = if b then b else (say s;b);
fun explain2 b s = if not b then b else (say s;b);
fun max(m,n) = if m>n then m else n;
fun p1(x,y) = x;
fun p2(x,y) = y;
(* kill empty errors *)
fun Hd default nil = (say "underflow error in Hd";default) |
Hd default (x::L) = x;
fun Tl nil = (say "underflow error in Tl";nil) |
Tl x = tl x;
(* is an item in a list? *)
fun find0 s nil = false |
find0 s (t::L) = if s=t then true else find0 s L;
fun drop0 s nil = nil |
drop0 s (t::L) = if s=t then drop0 s L
else (t::(drop0 s L))
fun add0 s L = s::(drop0 s L);
fun merge0 L nil = L |
merge0 nil M = M |
merge0 ((s)::L) ((t)::M) =
add0 s (add0 t (merge0 L M));
(* look up an item in a labelled list *)
fun find default s nil = default |
find default s ((t,u)::L) = if s=t then u else find default s L;
fun drop s nil = nil |
drop s ((t,u)::L) = if s=t then drop s L
else (t,u)::(drop s L );
fun findfancy default s LREF =
let val FOUND = find default s (!LREF) in
if FOUND = default then default
else (LREF:= (s,FOUND)::(drop s (!LREF)); FOUND) end;
fun add s t L = (s,t)::(drop s L);
fun extendslist nil L = true |
extendslist L nil = false |
extendslist L M = hd(rev L) = hd(rev M)
andalso extendslist (rev(tl(rev L))) (rev(tl(rev M)));
(*
\end{verbatim}
\newpage