-
Notifications
You must be signed in to change notification settings - Fork 0
/
computation.spad
3042 lines (2688 loc) · 111 KB
/
computation.spad
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
)if false
\documentclass{article}
\usepackage{axiom}
\usepackage{url}
\begin{document}
\title{computation related mathematical structures}
\author{Martin J Baker}
\maketitle
\begin{abstract}
In this pamphlet are implementations of various mathematical
structures related to computation and type theory. These are:
\begin{itemize}
\item lambda.spad
\item ski.spad
\item ILogic.spad
\end{itemize}
The aim here is to model and study abstract models of computing such
as lambda-calculus, combinators and intuitionistic logic. These abstract
mathematical structures, which were discovered before modern computers,
are worthy of study in their own right.
We are less interested in using these domains to do actual computing
than to study the structures and their relationships to each
other. If we only wanted to evaluate a lambda function then there are
many other programs that could you that. So, in these domains such as
lambda, we do not attempt to reduce as soon as it is constructed
(although redux can be called if explicitly required). Instead we
leave it as a structure to be combined, transformed, compared and
studied.
Computing is often defined in terms of physical quantities such as
'information' and 'entropy' but here we are discussing computing in
mathematical terms [see 11]. In the 1930s, Kurt Godel, Alonzo Church,
Emil Post, and Alan Turing independently gave formal notions of
computation in terms of specific mathematical structures.
\begin{itemize}
\item Godel defined computation in terms of the evaluations of
recursive functions.
\item Church defined computation in terms of the evaluations of
lambda expressions,
\item Post defined computation as series of strings successively
rewritten according to a given rule set.
\item Turing defined computation in terms of the Turing machine,
the sequence of states of an abstract machine (usually explained
in terms of a physical machine with a control unit and a tape).
\end{itemize}
Can we abstract away from these specific structures to get a
more general definition?
A possible computational model is representation-transformation.
Provided that we can transform a given representation in a
sufficiently rich way (that is : Turing complete) then it can
represent any computation that is possible. There are limits to
what computations are possible as shown by applying Godel's
incompleteness theorems to computation. This gives rise to the
concept of undecidable problems.
So a 'representation' may for example be a string, but since
the string may contain brackets we can represent tree structures
and many other structures. Tree structures do seem to be a very
good representation for modeling many computations in an efficient
way.
Our 'transformation' may be a sequence of steps, or an algorithm,
or a function.
We are also interested in the relationship to logic, this approach
is based on the ideas of Philip Wadler [1] and others.
A program that uses this approach is Djinn [2]
The arguments of these 'computation' domains are functions and
combinators and the operation is usually function application,
although other operations such as function composition and
cartesian product may also be supported.
For combinators the number of arguments is variable and so function
application is non-associative hence it is represented by binary
tree structure.
For Lambda calculus function application is the opposite process
to the lambda function, that is lambda creates a function with
a bound variable and function application removes the bound variable.
Since we have bound and unbound variables so again non-associative
and represented by binary tree structure.
I am also working on a framework for function composition.
\url{http://www.euclideanspace.com/prog/scratchpad/mycode/discrete/homset/}
Function composition is associative and so can be
represented by a list structure. It therefore has different
mathematical properties to the structures studied here but they
can be related to closed cartesian categories.
These domains all represent systems of logic and are constructed from
tree structures and act on tree structures. These 'tree logics' seem
to 'generate' other algebras, for instance, below, we see that
this lambda structure:
\begin{verbatim}
\x.\y.y x
\end{verbatim}
can be converted to this SKI structure:
\begin{verbatim}
S(K(SI))(S(KK)I)
\end{verbatim}
This allows us to 'abstract' the definition, in that it takes a
definition in terms of arbitrary variables and it converts to a
definition without arbitrary variables. So these structures are
equivalent and they both reverse two operands. That is they generate
an $n$-ary to $n$-ary function, in this case:
\begin{verbatim}
(x, y) -> (y, x)
\end{verbatim}
This seems to be a 'monad' and it would be interesting to see if
these domains could be implemented as instances of a monad (a
monad in category theory terms, not the current FriCAS monad
category).
For more details see:
\url{http://www.euclideanspace.com/prog/scratchpad/mycode/computation/}
\begin{itemize}
\item Tutorial for lambda calculus:
\url{http://www.euclideanspace.com/prog/scratchpad/mycode/computation/lambda/}
\item Tutorial for SKI calculus:
\url{http://www.euclideanspace.com/prog/scratchpad/mycode/computation/ski/}
\item Tutorial for Intuitionistic Logic
\url{http://www.euclideanspace.com/prog/scratchpad/mycode/discrete/logic/intuitionistic/}
\item Tutorial for utilities to coerce between computation domains
\url{http://www.euclideanspace.com/prog/scratchpad/mycode/computation/utility/}
\item Tutorial for FriCAS source code generation
\url{http://www.euclideanspace.com/prog/scratchpad/mycode/computation/codeGen/}
\end{itemize}
\end{abstract}
\eject
\tableofcontents
\eject
\section{To Do}
There are improvements and corrections required for later versions
of this software.
\begin{enumerate}
\item Overall architecture:
Investigate possibility of basing implementation on Kernel or
SExpression domains. Or we could extend some non-associative or
binary tree structure class?
\item Possible generalisations:
\begin{itemize}
\item I am working on a function composition (hom-set) domain
\url{http://www.euclideanspace.com/prog/scratchpad/mycode/discrete/homset/}
and it would be interesting to implement these
as a special (non-associative) version of that.
\item It would be interesting to see if these domains could be implemented
as instances of a monad.
\item I would also like to extend this to investigate abstract structures
such as 'tree automata'.
\end{itemize}
\item Need to improve and correct intuitionistic logic domain.
\begin{itemize}
\item investigate change of meaning of '=' for intuitionistic logic to
represent equivalence rather than equality.
\item implement more complete algorithm to decide if two (quantifier-free)
intuitionistic formulas are equivalent.
\item make sure we have all simplifications for implies, /\, \/ and ~.
\end{itemize}
\end{enumerate}
\section{Notation}
It would be useful to be able to use unicode, at the moment we can't
for compatibility reasons. When the restrictions on using unicode are
lifted then the following would be useful:
\begin{itemize}
\item lambda symbol
\item alpha (as in alpha-substitution)
\item beta (as in beta-equivalence)
\item bottom symbol for \verb'_|_'
\end{itemize}
Currently for logic symbols we use notation:
\verb'T, _|_, /\, \/, ~'
as opposed to other possibilities such as:
\begin{itemize}
\item \verb'true, false, and or'
\item \verb'0, 1, &, |'
\item \verb'join, meet, -'
\end{itemize}
This is because \verb'/\, \/, ~' are built-in
operators in FriCAS and are already used by Logic category.
Also this emphasizes the lattice-like structure of the intuitionistic
logic domain. Also some systems of logic seem to treat
\verb'_|_' as a logical contradiction rather than
false. The aim is to use symbols that are compatible with both logic and
lattice terminology.
Implication is notated by -> to suggest its link to 'function'
although perhaps this should be changed to more standard =>.
In lambda calculus the elements are functions. Function application
and composition is notated by space, that is
f g represents f(g)
In lisp notation this would be (f g). Although we can use brackets
they are only used to indicate precedence and not to specify a function
since all variables are assumed functions.
I have used the term 'function application' for the following form:
f : A->B g : A = f g : B
and the term 'function composition' for the following form:
f : B->C g : A->B = f g : A->C
\section{Related Structures}
If we want to cover the full range of mathematical structures related
to computing then we should really include Finite State Machines and
Automata. These are coalgebra-like structures which are related to the
algebra structures already included.
I was thinking about how to convert a finite state machine into a
instance of Lambda, then I realised this is just about implementing
stateful code in a pure functional language and Haskell has already
solved that problem - use monads (from my limited knowledge of
Haskell I get the impression, whatever the problem, the answer is to
use monads).
Perhaps FriCAS should have a lattice structure which could then be
extended into intuitionistic logic domain. I don't think partially
ordered set (setorder.spad.pamphlet) would be in the right form to
extend logic domains.
Also graph structures, for instance:
\begin{itemize}
\item A deductive system is a graph where the nodes are formulas and
the arrows are deductions or proofs.
\item The category of small graphs is a concrete category.
\end{itemize}
Many of the domains in this document are also tree structures.
\section{Category Theory}
Lambek and Scott 1988 [10] show that typed lambda-calculi and
cartesian closed categories are essentially the same and
intuitionistic logic is closely related to topos theory.
Also the discussion of monads above tends to indicate that they
need to be used here.
All of this suggests to me that all the code in this document would
benefit from being put into a wider framework based on category
theory.
\section{Variables}
Before we get on to the main domains we have some code to represent
variables in Lambda and Ski domains.
Since we are working in terms of functions then a variable will be a
function (possibly a constant function).
There is a category to represent these function variables and there are
two implementations of this category so far:
\begin{description}
\item[Untyped] Untyped represents an untyped variable in Lambda and Ski
domains, a variable has a name represented by a String.
\item[Typed] Typed represents an typed variable in Lambda and Ski
domains, a variable has a name represented by a String
and a type represented by intuitionistic logic.
\end{description}
\section{Variables Tutorial}
The two implementations : Untyped and Typed which can be constructed as
follows:
\begin{verbatim}
(1) -> var("x")$Untyped
(1) x
Type: Untyped
(2) -> var("x",proposition("a"))$Typed
(2) x:a
Type: Typed
(3) -> parseVar("x:(a->b)")$Typed
(3) x:(a->b)
Type: Typed
(4) -> parseVar("x:(a/\b)")$Typed
(4) x:(a/\b)
Type: Typed
(5) -> parseVar("x:(a\/b)")$Typed
(5) x:(a\/b)
Type: Typed
\end{verbatim}
(1) shows an untyped variable.
(2) shows a typed variable with a simple type "a"
In (3 to 5) we are entering a variable with a more complicated compound
types so it is easier to use parseVar to construct from
a string although this could have been built from raw types.
Note that the \verb': ' binds more tightly than \verb'->' so we need to put
brackets around \verb'a->b' so that its read as a type.
There are 3 ways to build up compound types:
\begin{itemize}
\item (3) shows \verb'a->b' which represents a function from a to b
\item (4) shows \verb'a/\b' which represents a type like Record(a, b)
\item (5) shows \verb'a\/b' which represents a type like Union(a, b)
\end{itemize}
We can nest these compound types to build up more complex types.
Variables are not usually constructed directly by the user but instead
are used by lambda and ski domains.
\section{Types}
There are various ways to interpret the concept of 'type' in the study
of both computation and in pure mathematical terms, for example:
\begin{itemize}
\item types as sets
\item types as abstractions (Reynolds)
\item types as relations (Wadler)
\item propositions as types (Curry-Howard)
\end{itemize}
We need to be able to model type theory and to do this flexibly.
We must build our own type domains rather than use the types used by
SPAD (although a possible future enhancement would be to support SPAD
types as a special case but not the general concept of type).
I am trying to develop an abstract model of general mathematical
structures here, not a sub-language within FriCAS, so I don't want to
over specify this. However I think it would be good to specify a model
which is as general as possible and can be expanded to model these
things.
So we need various levels of support for types depending on the
situation:
\begin{itemize}
\item untyped
\item simply typed - types are fixed and don't depend on anything else,
all we can do is check that types match up correctly.
\item extended types - we can refine simple types by using some or all
of the following extensions (level of support for these can be shown
graphically by a diagram known as the lambda cube)
\begin{itemize}
\item Type Operator (types depend on types) - For example : function types
or cartesian products.
\item Polymorphism (terms depend on types) - For example : we can define
functions which can be applied to parameters with different types.
\item Dependant Types (types depend on terms)
\end{itemize}
\end{itemize}
Initially, not all of these options are supported by this framework and
there are other issues, still to be resolved:
\begin{enumerate}
\item How do we handle (and should we handle) subtypes and the various
flavors of polymorphism? I was thinking of an additional type domain
within this category whose representation is a list of sub-types,
which in turn may have sub-sub-types. In other words a tree structure.
Most of the structures in this computation framework are already tree
structures so this fits the pattern.
\item How do we handle (and should we handle) compounded types such as
'List over String'?
\item It would be interesting to have a type domain in this category where
the types were SPAD types. This would be an exception, usually I
would prefer an abstract type model, but it may be possible to do
interesting things by mapping FriCAS to an abstract type model.
\item Could these type models be used outside of the computation
framework? For instance a set whose elements may be assigned different
types.
\end{enumerate}
One of the most important flavors to support is simply typed with
function types. This is because:
\begin{itemize}
\item It has enough power to represent most computations (although it
is not Turing complete - we would need fixpoint operator for that)
\item At this level there is an equivalence between Lambda calculus,
intuitionistic logic and closed cartesian categories which makes this
interesting.
\item We can infer types by using the Hindley-Milner type inference
algorithm.
\end{itemize}
\section{simply typed with type operator for function types
and cartesian products}
We can write formation rules for well typed terms (WTT) using the
following notation:
\begin{itemize}
\item x, y... (lower case) = variables
\item A, B ... (upper case, beginning alphabet) = types
\item M, N ... (upper case, middle alphabet) = metavariables for terms
\end{itemize}
Using this terminology the rules are:
\begin{itemize}
\item every variable x : A is a WTT.
\item Function creation:
if x : A is a variable and M : B is a WTT then \lambda x : A.M : A->B is a WTT
\item Function application:
if M : A -> B is a WTT and N : A is a WTT then M N : B is a WTT
\item Product creation:
if M : A is a WTT and N : B is a WTT then <M, N>: A \times B is a WTT
\item Product removal (first component):
if M : A \times B is a WTT then fst(M) : A is a WTT
\item Product removal (second component):
if M : A \times B is a WTT then snd(M) : B is a WTT
\end{itemize}
Now we will implement variables:
First a common category for the various variable types:
\section{category VARCAT VarCat}
)endif
)abbrev category VARCAT VarCat
++ Author: Martin Baker
++ Date Created: April 2011
++ Basic Operations:
++ Related Constructors: lambda, ski
++ Also See:
++ AMS Classifications:
++ Keywords:
++ References:
++ http://www.euclideanspace.com/prog/scratchpad/mycode/computation/
++ Description: VarCat represents a variable in Lambda and Ski domains.
++ Since we are working in terms of functions then a variable will be a
++ function (possibly a constant function)
++ a variable has a name represented by a String. If the Lambda or Ski
++ calculus is typed then the variable will also have a type.
VarCat() : Category == Join(Type, CoercibleTo(OutputForm)) with
var : (n : String) -> %
++ constructs variable with a name but no type.
var : (n : String, t : ILogic) -> %
++ constructs variable with a name and also a type.
getName : (v : %) -> String
++ returns the name of the variable.
getType : (v : %) -> ILogic
++ returns the type of the variable. This may be a compound type, for
++ instance ["a","b"] represents a->b a function from type a to type b
++ untyped implementations return []
toString : (v : %) -> String
++ returns the name and the type in string form.
parseVar : (t1 : String)-> %
++ construct a variable by parsing a string
parseVarTerm : (t1 : String, pin : NonNegativeInteger)-> Record(rft : %, pout : NonNegativeInteger)
++ construct a variable by parsing a string
++ pin is index to string at start of parse
++ pout is index to string at end of parse
"=":(x:%, y:%) -> Boolean
++ return true if equal, that is names are equal and if there is
++ a type then they must also be equal
coerce : (n : %) -> OutputForm
++ output
)if false
Next an implementation of the above category which is intended to work with
untyped variables:
\section{domain UNTYPED Untyped}
)endif
)abbrev domain UNTYPED Untyped
++ Author: Martin Baker
++ Date Created: April 2011
++ Basic Operations:
++ Related Constructors: lambda, ski
++ Also See:
++ AMS Classifications:
++ Keywords:
++ References:
++ http://www.euclideanspace.com/prog/scratchpad/mycode/computation/
++ Description: Untyped represents an untyped variable in Lambda and Ski
++ domains, a variable has a name represented by a String.
Untyped() : Exports == Implementation where
NNI==> NonNegativeInteger
PR ==> Record(rft : %, pout : NNI)
++ parse result includes string returned and new index
Exports ==> Join(VarCat(), CoercibleTo(OutputForm))
Implementation ==> add
Rep := Record(nme : String)
++ At the moment an untyped variable is represented by a string
++ representing the name of the variable.
++ So why wrap it in a Record structure.
++ The main reason is because I am planning to expand this as
++ explained in the text part of this pamphlet.
++ Also it seems like a good idea to keep a similar structure to
++ typed variables.
++ Also I could not get it to work unwrapped. I vaguely recall that
++ I couldn't get the constructor to return an unwrapped value, but
++ it may have been some other issue and that's not the main reason.
-- constructs variable with a name but no type
var(n : String) : % ==
[n]
-- since this is an untyped implementation the type value
-- is ignored
var(n : String, t : ILogic) : % ==
[n]
-- returns the name of the variable.
getName(v : %) : String ==
v.nme
-- returns the type of the variable.
-- Since this is an untyped variable it will always return an
-- empty string.
getType(v : %) : ILogic ==
logicT()
-- returns the name and the type in string form.
toString(v : %) : String ==
v.nme
-- construct a variable by parsing a string
-- pin is index to string at start of parse
-- pout is index to string at end of parse
parseVarTerm(t1 : String, pin : NNI) : PR ==
vnm:String := ""
pt : NNI := pin
ch := qelt(t1, pt)
--print(message "st ch=" << ch)
while alphanumeric?(ch) repeat
vnm := concat(vnm, ch)
pt := pt + (1::NNI)
if pt > maxIndex(t1) then
return [var(vnm), pt]
ch := qelt(t1, pt)
if ch = char(":") then
-- read type
--print(message "st ch=" << ch)
r : Record(rft : ILogic, pout : NNI) := parseIL2(t1, pt)$ILogic
pt := r.pout
-- since this instance is untyped we ignore result
[var(vnm), pt]
-- construct a variable by parsing a string
parseVar(t1 : String) : % ==
r : PR := parseVarTerm(t1, 1::NNI)
r.rft
-- return true if equal, that is names are equal (since there are
-- no types that is all that is required) in the case of typed
-- implementations then they would also have to be equal.
x = y ==
x.nme = y.nme
-- output - display without quotes
coerce(n : %) : OutputForm ==
message(n.nme)
)if false
Next an implementation of the VarCat category which is intended to work with
typed variables:
\section{domain TYPED Typed}
)endif
)abbrev domain TYPED Typed
++ Author: Martin Baker
++ Date Created: April 2011
++ Basic Operations:
++ Related Constructors: lambda, ski
++ Also See:
++ AMS Classifications:
++ Keywords:
++ References:
++ http://www.euclideanspace.com/prog/scratchpad/mycode/computation/
++ Description: Typed represents an typed variable in Lambda and Ski
++ domains, a variable has a name represented by a String and a type
++ represented by intutionistic logic.
Typed() : Exports == Implementation where
NNI==> NonNegativeInteger
PR ==> Record(rft : %, pout : NNI)
++ parse result includes string returned and new index
Exports ==> Join(VarCat(), CoercibleTo(OutputForm))
Implementation ==> add
Rep := Record(nme : String, typ : ILogic)
++ name of variable
++ and type of variable
-- constructs variable with a name but no type
var(n : String) : % ==
[n, logicT()]
-- in this constructor both name and type are supplied
var(n : String, t : ILogic) : % ==
[n, t]
-- returns the name of the variable.
getName(v : %) : String ==
v.nme
-- returns the type of the variable. This may be a compound type, for
-- instance ["a","b"] represents a->b a function from type a to type b
getType(v : %) : ILogic ==
v.typ
-- returns the name and the type in string form. This may be a compound
-- type, for instance ["a","b"] represents a->b a function from type a
-- to type b
toString(v : %) : String ==
s:String := concat([v.nme,":",toString(v.typ)])
s
-- construct a variable by parsing a string
-- pin is index to string at start of parse
-- pout is index to string at end of parse
parseVarTerm(t1 : String, pin : NNI) : PR ==
vnm:String := ""
pt : NNI := pin
ch := qelt(t1, pt)
--print(message "st ch=" << ch)
while alphanumeric?(ch) repeat
vnm := concat(vnm, ch)
pt := pt + (1::NNI)
if pt > maxIndex(t1) then
return [var(vnm), pt]
ch := qelt(t1, pt)
if ch = char(":") then
-- read type
--print(message "st ch=" << ch)
pt := pt + (1::NNI)
if pt > maxIndex(t1) then
return [var(vnm), pt]
r : Record(rft : ILogic, pout : NNI) := parseIL2(t1, pt)$ILogic
pt := r.pout
return [var(vnm, r.rft), pt]
[var(vnm), pt]
-- construct a variable by parsing a string
parseVar(t1 : String) : % ==
r : PR := parseVarTerm(t1, 1::NNI)
r.rft
-- return true if equal, that is names are equal (since there are
-- no types that is all that is required) in the case of typed
-- implementations then they would also have to be equal.
x = y ==
(x.nme = y.nme) and (x.typ = y.typ)
-- output -
coerce(n : %) : OutputForm ==
hconcat([message (n.nme),message ":",(n.typ)::OutputForm])$OutputForm
)if false
\section{lambda-Calculus}
for more details see:
\url{http://www.euclideanspace.com/prog/scratchpad/mycode/computation/lambda/}
\section{Notation}
Externally a fairly standard notation is used, as may be familiar to
someone using a textbook to study lambda-calculus, or as close as we can get
without using unicode. I have used the \verb'\' symbol to stand for lambda. At
some stage in the future it may be possible to use unicode lambda symbol
but not yet due to compatibility issues.
Internally the domain stores bound variables using De Bruijn index,
in most cases this should not concern the user as I/O uses string
names for variables. Converting bound variables internally to index
values means that the same variable name can be used, in different
lambda terms, without ambiguity and without the need for
alpha-substitution.
De Bruijn index which is a integer where
\begin{verbatim}
0 = inside current (inner) lambda term
1= next outer lambda term
2= next outer and so on
...
\end{verbatim}
We will see how this works in the tutorial below.
So internally the lambda-calculus is stored as a binary tree structure
using the following syntax:
\begin{verbatim}
<term> : := "\" var "."<term> | n | <term><term> | "("<term>")"
where:
\ = lambda
n = De Bruijn index which is a integer where,
0 = inside inner lambda term,
1= next outer lambda term,
2= next outer and so on.
var = a string representation of variable (this form is used for
unbound variables)
brackets can be used around whole terms.
\end{verbatim}
\section{Tutorial}
On this page we will be working with 'untyped' variables so we create
an instance called UNTYP to simplify notation:
\begin{verbatim}
(1) -> UNTYP := Lambda Untyped
(1) Lambda(Untyped)
Type: Type
\end{verbatim}
\section{Constructors}
First we can enter some variables, at the moment they are not inside
a lambda-term so they can't yet be bound, but later we can put them into
a lambda-term.
A numeric name is interpreted as a De Bruijn index when put inside
a lambda-term, although we don't need this notation for I/O unless we are
trying to avoid some ambiguity, because free and bound variables can
be constructed by giving the variable name as a string. So in (4) is
not yet a valid term on its own but it will be when put inside a
lambda-term, when this is done it will be given the name of the bound
variable rather than "0".
Internally a string name will be converted to a De Bruijn index when
put inside a matching lambda-term, otherwise it will be interpreted as a
free variable.
\begin{verbatim}
(2) -> v1 := lambda(var("x")$Untyped)$UNTYP
(2) x
Type: Lambda(Untyped)
(3) -> v2 := lambda(var("y")$Untyped)$UNTYP
(3) y
Type: Lambda(Untyped)
(4) -> v3 := lambda(0)$UNTYP
(4) 0
Type: Lambda(Untyped)
\end{verbatim}
This can be built up into more complex lambda terms by using compound
terms (as in (5)) and the lambda-term itself (as in (6)).
Each lambda-term can only have one bound variable, if more than one bound
variable is required then lambda-terms can be nested. lambda-term requires that
the bound variable be given a name.
\begin{verbatim}
(5) -> n1 := lambda(v1, v2)$UNTYP
(5) (x y)
Type: Lambda(Untyped)
(6) -> n2 := lambda(n1,var("x")$Untyped)$UNTYP
(6) (\x.(x y))
Type: Lambda(Untyped)
\end{verbatim}
In (7) \verb'x' is a the bound variable and so, when the lambda-term was created,
this will be converted to De Bruijn index, in (7) we call toString to
see the internal representation:
In (8) we see that when entered as a numeric index the bound variable
will still be displayed as a string.
In (9) and (10) we can see that the \verb'unbind' function can be used to
unbind the bound variable \verb'x' that is, although \verb'x' has the same string
value as the lambda term, it is treated as though it were different.
We can see this because the toString output does not have a index value.
In (11) we call \verb'bind' to re-bind it.
\begin{verbatim}
(7) -> toString(n2)$UNTYP
(7) "(\x.(0 y))"
Type: String
(8) -> n3 := lambda(v3,var("x")$Untyped)$UNTYP
(8) (\x.x)
Type: Lambda(Untyped)
(9) -> u2 := unbind(n2)$UNTYP
(9) (\x.(x y))
Type: Lambda(Untyped)
(10) -> toString(u2)$UNTYP
(10) "(\x.(x y))"
Type: String
(11) -> toString(bind(u2))$UNTYP
(11) "(\x.(0 y))"
Type: String
\end{verbatim}
So we can already construct any type of lambda term, however its a bit
tedious to construct complex lambda terms in this way, an easier way is
to use \verb'parseLambda' to construct the lambda term from a string. Again
we can enter variables as either alpha or numeric characters depending
on whether we want to specify the index value directly or allow the
code to generate it.
In (14) we can see the use of numeric terms to avoid the ambiguity
caused by nested lambda-terms with the same name.
\begin{verbatim}
(12) -> n4 := parseLambda("\x.\y. y x")$UNTYP
(12) (\x.(\y.(x y)))
Type: Lambda(Untyped)
(13) -> toString(n4)$UNTYP
(13) "(\x.(\y.(0 1)))"
Type: String
(14) -> n4a := parseLambda("\x.\x. 0 1")$UNTYP
(14) (\x.(\x'.(x x')))
Type: Lambda(Untyped)
(15) -> toString(n4a)$UNTYP
(15) "(\x.(\x.(0 1)))"
Type: String
(16) -> unbind(n4a)$UNTYP
(16) (\x.(\x'.(x x)))
Type: Lambda(Untyped)
\end{verbatim}
\section{beta-substitution}
The command: \verb'subst : (n, a, b)' substitutes \verb'a' for \verb'b'
in \verb'n' as follows:
\begin{verbatim}
(17) -> subst(n2, v2, v1)$UNTYP
(17) (\x.(x y))
Type: Lambda(Untyped)
(18) -> subst(n2, v1, v2)$UNTYP
(18) (\x.(x x))
Type: Lambda(Untyped)
\end{verbatim}
\section{Issues}
I realise that Axiom/FriCAS already has a way to create anonymous
functions using \verb'+->' in a lambda-calculus sort of way. But the aim here
is to represent lambda-calculus as a pure mathematical structure so that
we can experiment with the properties of this structure without the
messy features that are required for a practical computer language.
I also need a domain structure which is related to SKI combinators
and IntuitionisticLogic domain and can be coerced to and from these
other domain types as demonstrated on this page.
I also realise that this is written in SPAD which is written in
Lisp which is based on lambda-Calculus (perhaps it could later be
optimized by having direct calls to Lisp?)
Relationship to Other Domains
\section{Relationship to Other Domains}
lambda-calculus can be coerced to and from SKI combinators. For a tutorial
about how to coerce to/from this algebra see below.
\section{domain LAMBDA Lambda}
)endif
)abbrev domain LAMBDA Lambda
++ Author: Martin Baker
++ Date Created: March 2011
++ Basic Operations:
++ Related Constructors:
++ Also See:
++ AMS Classifications:
++ Keywords:
++ References:
++ http://www.euclideanspace.com/prog/scratchpad/mycode/computation/lambda/
++ An implementation of untyped lambda-calculus
Lambda(UT) : Exports == Implementation where
UT : VarCat
NNI==> NonNegativeInteger
PR ==> Record(rft : %, pout : NNI)
++ parse result includes term returned and new index
x<<y ==> hconcat(x::OutputForm,y::OutputForm)
++ macro to simplify output
Exports == CoercibleTo(OutputForm) with
lambda : (deBruijn : NNI) -> %
++ Constructs a reference to a bound variable from its deBruijn
++ index
lambda : (varNme : UT) -> %
++ Constructs a reference to a free variable
lambda : (x : %, y : %) -> %
++ Constructs a node containing multiple terms
lambda : (x : %, t1 : UT) -> %
++ Constructs lambda term and bind any variables with the name provided
parseLambda : (t1 : String) -> %
++ Constructs nested lambda terms from a string
++ notation assumes format like this:
++ <term> : := "\" var "."<term> | n | <term><term> | "("<term>")"
++ where:
++ \ = lambda (I would like to use unicode lambda symbol but
++ I would also like to keep maximum compatibility
++ with non-unicode versions of Lisp)
++ n = De Bruijn index which is a integer where, 1=inside inner
++ lambda term, 2= next outer lambda term, 3= next
++ outer and so on.
++ brackets can be used around whole terms.
parseTerm : (t1 : String, pin : NNI) -> PR
++ parseTerm is used by parseLambda. It would rarely be called
++ externally but it is here to allow it to call parseLambda
++ that is to allow circular calls
atom? : (n : %) -> Boolean
++ returns true if this is an atom, that is free or bound variable
++ otherwise return false if this is a compound or lambda definition
isCompound? : (n : %) -> Boolean
++ introspection: returns true if this is a compound term containing
++ two nodes
isLambda? : (n : %) -> Boolean
++ introspection: returns true if this is a lambda definition
isBoundNode? : (n : %) -> Boolean
++ introspection: returns true if this is a bound leaf node
isFreeNode? : (n : %) -> Boolean
++ introspection: returns true if this is a unbound leaf node
getBoundValue : (n : %) -> NNI
++ introspection: returns deBruijn index of bound variable in bound
++ leaf node
getVariable : (n : %) -> UT
++ introspection: returns value of unbound variable in unbound leaf
++ node or bound variable in lambda term
free? : (n : %) -> Boolean
++ if this is a lambda term then is it free, that is does
++ its variable appear in its expression
getChildren : (n : %) -> List %
++ returns 2 child nodes if this is a compound term
++ returns 1 child node if this is a lamda term
++ otherwise returns []
subst : (n : %, a : %, b : %) -> %
++ substitution of 'a' for 'b' in 'n'
bind : (n : %) -> %
++ if this is a lambda term then replace string name in sub-nodes
++ with De Bruijn index
unbind : (n : %) -> %
++ if this is a lambda term then replace De Bruijn index in sub-nodes
++ with string name
redux : (n : %) -> %
++ beta reduction - apply beta reduction recusivly to all subnodes
"=":(x:%, y:%) -> Boolean
++ return true if equal (deep search) that is: all terms
++ at all levels in tree must be alpha-equivalent to return true
++ That is the names, but not the deBruijn index, of the
++ bound variables can be different.
++ beta-equivalence is not implemented because it is not decidable.
toStringConven : (n : %, boundL : List String) -> String
++ return string representation using conventional notation, that is
++ deBruijn index is replaced by name using String value for bound variables.
++ notation assumes association to the left, in the absence of
++ brackets, the term to the left binds more tightly than the
++ one on the right.
toString : (n : %) -> String
++ return string representation using deBruijn index for
++ bound variables.
++ notation assumes association to the left, in the absence of
++ brackets, the term to the left binds more tightly than the
++ one on the right.
coerce : (n : %) -> OutputForm
++ output
Implementation == add
Rep := Union(_
boundTerm : Record(varBound : NNI), _
freeTerm : UT, _
compoundTerm : Record(c1 : %, c2 : %), _
lambdaTerm : Record(c3 : %, nm : UT)_
)
++ this allows us to represent potentially nested lambda
++ expressions as a tree structure where the nodes are
++ one of:
++ 1) boundTerm: Record(varBound: NNI)
++ This represents a bound variable using De Bruijn index.
++ varBound = De Bruijn index which is a integer where,
++ 1=inside inner lambda term
++ 2= next outer lambda term,
++ 3= next outer and so on.
++
++ 2) freeTerm: UT
++ This represents an unbound variable so De Bruijn index
++ is not used.
++
++ 3) compoundTerm: Record(c1: %, c2: %)
++ Adjacent nodes represent function application
++
++ 4) lambdaTerm: Record(c3: %, nm: UT)
++ when used the outer term should be a lambdaTerm although
++ lambda terms may also be nested within the tree structure
++ c3 holds the subnodes
++ nm is the bound term for this lambda node
++ There is only one bound term for each lambda node, if more
++ are required then lambda nodes can be nested.
-- Constructs a reference to a bound variable from its deBruijn
-- index
lambda(n1 : NNI) : % == [[n1]]
-- Constructs a reference to a free variable from its name
lambda(var : UT) : % == [var]