-
Notifications
You must be signed in to change notification settings - Fork 24
/
i1lambda.tex
1608 lines (1515 loc) · 66.6 KB
/
i1lambda.tex
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
% Diese Datei ist Teil des Buchs "Schreibe Dein Programm!"
% Das Buch ist lizenziert unter der Creative-Commons-Lizenz
% "Namensnennung - Weitergabe unter gleichen Bedingungen 4.0 International (CC BY-SA 4.0)"
% https://creativecommons.org/licenses/by-sa/4.0/deed.de
\chapter{Exkurs: Der $\lambda$-Kalkül}
\label{chap:lambda}
\newcommand{\lrm}{\mathrm}
\index{Lambda-Kalkül@$\lambda$-Kalkül}
In diesem Kapitel geht es um eine wichtige Grundlage
für die Programmierung, die unter anderem erklärt, wie die Auswertung
eines Programms funktioniert. Das haben wir bisher nur
umgangssprachlich gemacht und dabei einige subtile Aspekte der
Auswertung gar nicht oder nur unpräzise erläutert. Das wollen wir in
diesem Kapitel korrigieren.
Dazu benutzen wir den sogenannten \textit{$\lambda$-Kalkül},
ausgeschrieben: \textit{Lambda-Kalkül}, der formal fasst, was der
Stepper in DrRacket visualisiert. Das "<Lambda"> im Namen des Kalküls
ist die Vorlage für das \lstinline{lambda} in unseren Lehrsprachen.
\section{Was hat ein alter "<Kalkül"> mit Programmieren zu tun?}
Vielleicht hast Du Dich schon gefragt, warum \lstinline{lambda}
eigentlich so heißt und nicht \lstinline{function} oder so. In Wort
"<Lambda"> steckt der historische Schlüssel für viele Aspekte der
Programmierung heute. Bevor wir in die Technik dazu einsteigen, fasst
dieser Abschnitt kurz die Historie zusammen, die erklärt, warum der
$\lambda$-Kalkül so große Bedeutung für das Programmieren hat.
Als in den 1940er Jahren die ersten Programmiersprachen entwickelt
wurden, haben ihre Erfinderinnen und Erfinder nach Inspiration in
existierenden "<Sprachen"> gesucht. Es gab zwei naheliegende
Ausgangspunkte:
%
\begin{itemize}
\item schriftliche natürliche Sprachen
\item Mathematik
\end{itemize}
%
Beide Ansätze haben zu unterschiedlichen Traditionen in der
Entwicklung von Programmiersprachen geführt, die sich gegenseitig
beeinflusst haben.
Die Mathematik ist deswegen eine wichtige Inspiration, weil die
Menschheit sie schon Jahrhunderte vor der Erfindung von Computern
benutzt hat, um Sachverhalte unmissverständlich und eindeutig zu
beschreiben: So etwas benötigen wir auch für die Programmierung.
Mit unseren Lehrsprachen befinden wir uns in der Tradition der
Mathematik. Der heute berühmte Informatiker John McCarthy arbeitete
den späten 1950er Jahren an einem System, das "<gesunden
Menschenverstand"> abbilden sollte.
Als Teil dieses Projekts entwickelte er die Programmiersprache
LISP~\cite{McCarthy1960}. Als ausgebildeter Mathematiker suchte
McCarthy dafür Inspiration in der Mathematik. Mathematische Notation
hat das Problem, dass die gleiche
Notation in unterschiedlichen Kontexten für unterschiedliche
Zwecke benutzt wird. Allgemein mathematische Notation für den Computer
verständlich zu machen, ist darum extrem aufwendig und war für
McCarthy undenkbar, der es mit Computern zu tun hatte, die nur einen
winzigen Bruchteil der heutigen Rechen- und Speicherkapazität haben.
McCarthy brauchte deshalb einen besonders einfachen mathematischen
Formalismus, der trotzdem mächtig genug war, mathematische Funktionen
ausdrücken zu können. Für die Definition von Funktionen war (und ist)
mathematische Notation aber besonders vielfältig. (Manche würden
"<schlampig"> sagen.) McCarthy stellte fest, dass ein
mathematischer Kalküls aus den 1930er Jahren ihm gerade die benötigte
Notation für die Definition von Funktionen und ihrer Anwendung gab
und entwickelte LISP deshalb auf Grundlage dieses Kalküls.
Dieser Kalkül war der $\lambda$-Kalkül des Mathematikers Alonzo
Church~\cite{Church1941}, der ihn als Beitrag zu den Grundlagen der
Logik entwickelt hatte. (Das $\lambda$ selbst war durch einen Unfall
entstanden, weil Churchs Schriftsetzer bei der Notation $\hat{x}$
fälschlicherweise glaubte, statt des "<Dachs"> ein $\lambda$ zu
erkennen.) Die Computer der damaligen Zeit konnten nur römische
Buchstaben darstellen, deshalb schrieb McCarthy das $\lambda$ als Wort
\lstinline{lambda} aus.
LISP hatte bereits eine Notation, die der Notation der Lehrsprachen
sehr ähnelt: Alles wurde vollständig geklammert, und der Operator
stand immer vorn. (McCarthy hatte auch noch eine andere Notation
entwickelt, die sich aber als unpopulär erwies.) Auch die Idee der
Listen wie wir sie heute kennen war damals schon genauso vorhanden.
("<LISP"> steht für "<list processing">.)
Der $\lambda$-Kalkül fiel noch anderen als mögliche Grundlage für die
Entwicklung von Programmiersprachen auf. Besonders einflussreich war
Peter Landins Aufsatz mit dem großspurigen Titel \textit{The next 700
programming languages}~\cite{Landin1966}, in dem er vorschlug, den
$\lambda$-Kalkül als Grundlage für eine ganze Familie von
Programmiersprachen zu benutzen.
LISP und die in Landins Aufsatz vorgeschlagene Sprache ISWIM ("<If you
See What I Mean">) standen Patin für fast alle modernen
Programmiersprachen. Es gab zwar seit den 1960er Jahren viele
alternative Ansätze für Programmiersprachen mit anderen Grundlagen
(zum Beispiel die objektorientierte Programmierung), bei deren
Entwicklung auf den $\lambda$-Kalkül verzichtet wurde. Bei fast allen
von ihnen stellten ihre Entwicklerinnen und Entwickler aber später
fest, dass die Elemente des $\lambda$-Kalküls beziehungsweise der
darauf aufbauenden Programmiersprachen eine Bereicherung sind und
fügten sie hinzu.
Wenn Du also den $\lambda$-Kalkül kennst, kennst Du die Grundlage für
die moderne Programmierung. Der Weg dahin benötigt etwas Geduld.
Insbesondere müssen wir noch etwas mehr mathematische Notation
einführen, die Dir aber helfen wird, falls Du mal einen der
einschlägigen Aufsätze oder Bücher zu diesem Thema verstehen möchtest.
\section{Die Sprache des $\lambda$-Kalküls}
\label{sec:sprache}
Der $\lambda$-Kalkül bricht die Elemente der Programmierung auf nur
das absolut notwendige herunter: Je weniger Elemente es gibt, desto
weniger müssen wir erklären und definieren.
Was also ist an unseren Lehrsprachen absolut notwendig? In
Kapitel~\ref{cha:whats-programming} haben wir angefangen mit einer
Zahl. Brauchen wir also Zahlen? Wir haben viele Programme ohne
Zahlen geschrieben. Wir werden sehen, dass wir
Zahlen mit anderen Elementen der Sprache nachbilden können.
Das nächste Element sind Funktionsanwendungen. Die gibt es nun
wirklich in jedem Programm. Funktionsanwendungen wiederum machen nur
dann Sinn, wenn wir auch Funktionen herstellen können: Wir brauchen
also Abstraktionen. Und da zu jeder Abstraktion auch eine oder
mehrere Variablen gehören, brauchen wir auch Variablen. Drei
fundamentale Ideen also:
%
\begin{itemize}
\item Applikation
\item Abstraktion
\item Variable
\end{itemize}
%
Variablen sind Namen und alle drei Ideen des $\lambda$-Kalküls haben
fundamental mit Variablen zu tun: Eine Abstraktion
\emph{bindet} eine Variable, eine Applikation setzt für eine Variable
etwas ein.
Wir beschränken uns bei Anwendung und Abstraktion sogar auf einen
einzelnen Parameter. Alle
anderen Elemente unserer Programmiersprachen können wir nachbilden,
indem wir sie in diese drei Konstrukte übersetzen. Sie
bilden das Rückgrat des $\lambda$-Kalküls.
Damit es losgeht, benötigen wir zunächst eine \textit{Sprache} für den
Kalkül. Die definieren wir mit Hilfe einer Grammatik. (Das ist
die Notation für induktive Definitionen, die wir in
Abschnitt~\ref{sec:grammatik} auf Seite~\pageref{sec:grammatik}
eingeführt haben.)
In der Definition taucht der Begriff \textit{abzählbare
Menge}\index{abzählbare Menge} auf. Der Begriff "<abzählbar"> ist
zwar technisch notwendig, Du benötigst ihn aber nicht für Verständnis
und Intuition des $\lambda$-Kalküls, kannst ihn also ignorieren.
Falls es Dich doch interessiert: Das Adjektiv "<abzählbar"> an
einer Menge bedeutet, dass die Menge unendlich groß ist und dass die
Menge durchnummeriert oder "<abgezählt"> werden kann. Hier geht es um
die abzählbare Menge der Variablen, welche den Variablen in Programmen
entsprechen. Die könnten wir auch durchnummerieren, indem wir den
Buchstaben Nummern geben.
\begin{definition}[Sprache des $\lambda$-Kalküls
$\mathcal{L}_{\lambda}$]
Sei $V$ eine abzählbare Menge von Variablen.
Die Sprache des $\lambda$"=Kalküls, die Menge der
\textit{$\lambda$-Terme\index{Lambda-Term@$\lambda$-Term}},
$\mathcal{L}_{\lambda}$\index{L@$\mathcal{L}_{\lambda}$}, ist
durch folgende Grammatik definiert:
\begin{grammar}
\meta{$\mathcal{L}_{\lambda}$} \: \meta{$V$}
\> \| (\meta{$\mathcal{L}_{\lambda}$} \meta{$\mathcal{L}_{\lambda}$})
\> \| ($\lambda$\meta{$V$}.\meta{$\mathcal{L}_{\lambda}$})
\end{grammar}
%
Ein $\lambda$-Term der Form $(e_0~e_1)$ heißt
\textit{Applikation\index{Applikation}}. Ein Term der Form
$(\lambda v.e)$ heißt \textit{Abstraktion\index{Abstraktion}}, wobei
$v$ \textit{Parameter\index{Parameter} der Abstraktion} heißt und
$e$ \textit{Rumpf\index{Rumpf}}.
\end{definition}
%
Hier sind ein paar Beispiele für $\lambda$-Terme. In diesen konkreten
Beispielen haben wir als Variablen $\lrm x$, $\lrm y$ und $\lrm f$
verwendet. Du solltest der Auswahl dieser Buchstaben keine Bedeutung
beimessen. Wir hätten genauso gut $\lrm a$, $\lrm b$ und $\lrm c$
verwenden können.
%
\begin{displaymath}
\begin{array}{rl}
\lrm x & \textrm{Variable}\\
(\lrm f~\lrm x) & \textrm{Applikation}\\
((\lrm f~\lrm x)~(\lambda\lrm x.\lrm x)) & \textrm{Applikation}\\
(\lambda \lrm x.(\lambda \lrm y.(\lrm x~\lrm y)) & \textrm{Abstraktion}\\
(\lambda \lrm x.(\lrm f~\lrm x)) & \textrm{Abstraktion}\\
(\lambda \lrm x.(\lrm f~(\lambda \lrm x~\lrm x)) & \textrm{Abstraktion}\\
((\lambda \lrm x.\lrm x)~(\lambda \lrm x.\lrm x))& \textrm{Applikation}
\end{array}
\end{displaymath}
%
Wir haben jeweils dazugeschrieben, um welche Art Term es sich handelt. Das
entspricht der Klausel der Grammatik für $\lambda$-Terme. Bei
$\lrm x$ sollte klar sein, dass es sich um eine Variable handelt.
Sowohl Applikationen als auch Abstraktionen fangen mit einer öffnenden
Klammer an. Bei Abstraktionen wird die Klammer aber direkt von einem
$\lambda$ gefolgt: Entsprecht sind die nächsten beiden Terme
Applikationen, die drei darauf Abstraktionen. Der letzte Term ist
eine Applikation~-- nach der öffnenden Klammer steht ein weitere
Klammer, das $\lambda$ kommt erst danach.
%
\begin{aufgabe}
Klassifiziere, ob es sich jeweils um eine
Variable, eine Applikation oder eine Abstraktion handelt:
\begin{displaymath}
\begin{array}[b]{c}
\lrm y\\
(\lrm x~\lrm y)\\
(\lambda \lrm x.\lrm y)\\
((\lambda \lrm x.\lrm y)~f)\\
\lrm f\\
(\lrm f~(\lambda \lrm x.\lrm y))
\end{array}\tag*{$\square$} % hack because \begin{array}[b] and \qed
% don't get along
\end{displaymath}
\end{aufgabe}
%
Manchmal sprechen wir aber auch über
$\lambda$-Terme einer bestimmten Form im Allgemeinen, zum Beispiel
steht oben in der Definition "<$\lambda$-Term der Form
$(e_0~e_1)$">. Damit sind \emph{alle} $\lambda$-Terme gemeint, die aus der
zweiten Regel der Grammatik stammen, also $e_0$ und $e_1$ beliebige
$\lambda$-Terme sein können. Hier sind einige Beispiele für
$\lambda$-Terme dieser Form:
%
\begin{displaymath}
\begin{array}{c}
(\lrm f~\lrm x),
(\lrm f~(\lambda \lrm x.\lrm x)),
((\lambda \lrm x.\lrm x)~(\lambda \lrm x.\lrm x))
\end{array}
\end{displaymath}
%
In der Definition sind außerdem Terme "<der Form $(\lambda v.e)$">
erwähnt, das könnte zum Beispiel sein:
%
\begin{displaymath}
\begin{array}{c}
(\lambda \lrm x.\lrm x),
(\lambda \lrm y.\lrm x),
(\lambda \lrm x.\lrm x~\lrm x),
(\lambda \lrm x.(\lrm f~\lrm x)~\lrm y),
(\lambda \lrm y.\lrm x)
\end{array}
\end{displaymath}
%
Du sieht, der Buchstabe $v$ in $(\lambda v.e)$ steht für eine Variable,
also zum Beispiel für $\lrm x$, $\lrm y$ oder $\lrm f$. Wenn Du genau hinschaust,
siehst Du, dass beide sich typografisch unterscheiden: Die
"<normalen"> Variablen $\lrm x$, $\lrm y$, $\lrm f$ sehen aus wie
reguläre Buchstaben, während $v$ in Kursivschrift gesetzt ist.
Das $v$ ist also eine Variable, die für eine andere Variable steht~--
eine sogenannte \textit{Metavariable}.\index{Metavariable}
In diesem Kapitel steht die Metavariable $e$ für einen
beliebigen $\lambda$-Term, $u$, $v$ und $w$ sind immer Metavariablen.
Das ist aber reine Konvention: Wir hätten genauso gut irgendwelche
anderen Buchstaben wählen können.
In Publikationen werden $\lambda$-Termen oft abgekürzt, entsprechend
machen wir das hier. Das geschieht vermutlich aus Faulheit, ist aber
erstmal gewöhnungsbedürftig. Die erste Form der Abkürzung lässt
Klammern weg. Statt des vollständig geklammerten Terms
\begin{displaymath}
(\lambda \lrm f.(\lambda \lrm x.(\lrm f~\lrm x)))
\end{displaymath}
%
werden wir das hier schreiben:
%
\begin{displaymath}
\lambda \lrm f.\lambda \lrm x.\lrm f~\lrm x
\end{displaymath}
%
Prinzipiell lässt sich dieser Term unterschiedlich
klammern: $(\lambda \lrm f.((\lambda \lrm x.\lrm f)~\lrm x))$, $(\lambda
f.(\lambda \lrm x.(\lrm f~\lrm x)))$ oder $((\lambda \lrm f.\lambda
\lrm x.\lrm f)~\lrm x)$. Wir verwenden aber die gängige Konvention,
dass der Rumpf einer Abstraktion sich so weit wie möglich nach
rechts erstreckt. Damit steht der ungeklammerte Term eindeutig für
den vollständig geklammerten Term, der darüber steht.
Die Terme der Form $\lambda v.e$ sind auf einen Parameter
beschränkt. Es gibt aber zwei weitere Abkürzungen
in der Notation von $\lambda$-Termen:
%
\begin{itemize}
\item $\lambda x_1 \ldots x_n.e$ steht für $\lambda x_1.(\lambda
x_2.(\ldots\lambda x_n.e)\ldots)$.
\item $e_0 \ldots e_n$ steht für $(\ldots(e_0~e_1)~e_2) \ldots e_n)$.
\end{itemize}
%
$\lambda \lrm{fxy}.\lrm f~\lrm x~\lrm y$ ist also eine andere Schreibweise für
den Term $(\lambda \lrm f.(\lambda \lrm x.(\lambda \lrm y.((\lrm f~\lrm x)~\lrm y))))$.
Hier sind weitere Beispiele für Terme und ihre Abkürzungen:
%
\begin{displaymath}
\begin{array}{rl}
(\lrm f~\lrm x) & \lrm f~\lrm x\\
(\lrm f~\lrm x)~\lrm y & \lrm f~\lrm x~\lrm y\\
(\lambda \lrm x.(\lrm x~\lrm~x)) & \lambda \lrm x.\lrm x~\lrm x\\
(\lambda \lrm x.((\lrm x~\lrm y)~\lrm z)) & \lambda \lrm x.\lrm
x~\lrm y~\lrm z\\
(\lambda \lrm x.(\lambda~\lrm y.(\lrm x~\lrm y))) & \lambda \lrm x\lrm y.\lrm x~\lrm y
\end{array}
\end{displaymath}
%
\begin{aufgabe}
Schreibe für die folgenden Abkürzungen die "<vollständigen">
$\lambda$-Terme auf:
\begin{displaymath}
\begin{array}{c}
\lrm f~\lrm x~\lrm y~\lrm z\\
\lambda \lrm x\lrm y\lrm z.\lrm x~\lrm y~\lrm z\\
((\lambda \lrm x\lrm y.\lrm x~\lrm y)~\lrm a~\lrm b)
\end{array}
\end{displaymath}
\end{aufgabe}
\section{$\lambda$-Intuition}
Es ist kein Zufall, dass die Lehrsprachen genau die gleichen Begriffe verwenden
wie der $\lambda$-Kalkül. Ein Lambda-Ausdruck mit einem
Parameter entspricht einer Abstraktion im $\lambda$-Kalkül,
und die Applikationen in den Lehrsprachen entsprechen den Applikationen im
$\lambda$-Kalkül. Die Lehrsprachen wurden bewusst auf dem
$\lambda$-Kalkül aufgebaut.
Die Intuition für die $\lambda$-Terme ist ähnlich wie in
den Lehrsprachen: Eine Abstraktion steht für eine mathematische Funktion,
speziell für eine solche Funktion, die sich durch ein Computerprogramm
berechnen lässt. Eine Applikation steht für
die Anwendung einer Funktion, und eine Variable bezieht sich auf den
Parameter einer umschließenden Abstraktion und steht für den Operanden
der Applikation.
Wichtig ist aber: Das ist an diesem Punkt nur eine Intuition. Wir
haben bisher nur die Sprache des $\lambda$-Kalküls definiert, nicht
aber, was die $\lambda$-Terme bedeuten oder wie sie sich verhalten.
Bemerkenswert am $\lambda$-Kalkül ist, dass es dort \emph{nur}
Funktionen gibt, noch nicht einmal Zahlen, \lstinline{#t},
\lstinline{#f} oder
zusammengesetzte Daten. Darum erscheint die Sprache des Kalküls auf den
ersten Blick noch spartanisch und unintuitiv: So unmittelbar lässt sich
noch nicht einmal eine Funktion hinschreiben, die zwei Zahlen addiert~--
schließlich gibt es keine Zahlen. Wie sich jedoch weiter unten in
Abschnitt~\ref{sec:lambdaprog} herausstellen wird, lassen sich all diese
Dinge durch Funktionen nachbilden.
Noch etwas: Die Funktionen haben auch nur jeweils
einen Parameter. Dies ist aber keine wirkliche Einschränkung:
Funktionen mit mehreren Parametern können wir
schönfinkeln\index{schönfinkeln}, um aus ihnen mehrstufige
Funktionen mit jeweils einem Parameter zu machen, wie schon in
Abschnitt~\ref{sec:currying} auf Seite~\pageref{sec:currying}.
\section{Wie funktionieren Variablen?}
Im $\lambda$-Kalkül dreht sich (fast) alles um Variablen. Die
funktionieren nach dem gleichen Prinzip wie bei den Lehrsprachen, der
lexikalischen Bindung. Die lexikalische Bindung haben wir in
Abschnitt~\ref{sec:lexikalische-bindung} auf
Seite~\pageref{sec:lexikalische-bindung} behandelt.
Kurz zur
Erinnerung: Das
Vorkommen einer Variable $v$ als $\lambda$-Term gehört immer zur
innersten umschließenden Abstraktion $\lambda v.e$, deren Parameter
ebenfalls $v$ ist. Bei $\lambda \lrm x.\lrm y~(\lambda \lrm y.\lrm y))$
ist also das erste $y$ das freie, während das zweite $y$ durch
die zweite Abstraktion gebunden wird.
Außerdem: Wenn es für eine Variable keine Abstraktion gibt, die diese
bindet, heißt die Variable \textit{frei}.
Um Bindung und "<Freiheit"> präzise zu definieren, brauchen wir zwei
Hilfsfunktionen für den Umgang mit Variablen.
%
\begin{definition}[Freie und gebundene Variablen]
Die Funktionen $\free$ und $\bound$ liefern für einen
$\lambda$-Terms die Mengen seiner \emph{freien} beziehungsweise seiner
\emph{gebundenen} Variablen. \index{Variable!frei}\index{freie
Variable}\index{Variable!gebunden}\index{gebundene Variable}%
\index{free@$\free$}\index{bound@$\bound$}
%
\begin{align*}
\free(e) & \deq
\begin{cases}
\{ v \} & \text{falls } e = v\\
\free(e_0) \cup \free(e_1) & \text{falls } e = e_0~e_1\\
\free(e_0) \setminus \{v\} & \text{falls } e = \lambda v.e_0
\end{cases}\\
\bound(e) & \deq
\begin{cases}
\varnothing & \text{falls } e = v\\
\bound(e_0) \cup \bound(e_1) & \text{falls } e = e_0~e_1\\
\bound(e_0) \cup \{v\} & \text{falls } e = \lambda v.e_0
\end{cases}
\end{align*}
%
Ein
$\lambda$-Term $e$ heißt \emph{abgeschlossen\index{abgeschlossen}}
falls $\free(e)=\varnothing$.
\end{definition}
%
Einige Beispiele:
%
\begin{eqnarray*}
\free(\lambda \lrm x.\lrm y) &=& \{\lrm y\}\\
\bound(\lambda \lrm x.\lrm y) &=& \{\lrm x\}\\
\free(\lambda \lrm y.\lrm y) &=& \varnothing\\
\bound(\lambda \lrm y.\lrm y) &=& \{\lrm y\}\\
\free(\lambda \lrm x.\lambda \lrm y.\lambda \lrm x.\lrm x~(\lambda \lrm z.\lrm a~\lrm y)) &=& \{ \lrm a\}\\
\bound(\lambda \lrm x.\lambda \lrm y.\lambda \lrm x.\lrm x~(\lambda z.\lrm a~\lrm y)) &=& \{ \lrm x,\lrm y,\lrm z\}
\end{eqnarray*}
%
Die Intuition ist folgende: Eine Abstraktion in einem $\lambda$-Term
\emph{bindet} eine Variable innerhalb seines Rumpfes. Eine freie
Variable hingegen ist eine, die nicht durch eine Abstraktion gebunden
ist. Darum bildet $\free$ bei einer Abstraktion die Differenz aus den
freien und den gebundenen
Variablen. Dahingegen sammelt $\bound$ alle Variablen aus
Abstraktionen auf. Wenn man $\free$ und $\bound$ eines Terms als
$\free(e)\cup\bound(e)$ zusammennimmt, bekommt man alle Variablen
eines $\lambda$-Terms.
In einem Term kann die gleiche Variable sowohl frei als auch gebunden
vorkommen:
%
\begin{eqnarray*}
\free(\lambda \lrm x.\lrm y~(\lambda \lrm y.\lrm y)) &=& \{ \lrm y\} \\
\bound(\lambda \lrm x.\lrm y~(\lambda \lrm y.\lrm y)) &=& \{ \lrm x,
\lrm y\}
\end{eqnarray*}
%
Das $\lrm y$ taucht innerhalb \emph{und} außerhalb einer bindenden
Abstraktion auf. Das Frei- und Gebundensein bezieht sich also
immer auf bestimmte Vorkommen\index{Vorkommen} einer Variablen in
einem $\lambda$-Term.
%
\begin{aufgabe}
Schreibe auf, was bei folgenden Anwendungen von $\free$ und $\bound$
herauskommt:
\begin{displaymath}
\begin{array}[b]{c}
\free(\lrm x~\lrm y)\\
\free(\lambda \lrm x.\lrm x~\lrm y)\\
\free(\lambda \lrm x\lrm y.\lrm x~\lrm y)\\
\free((\lambda\lrm x.\lrm y)~\lrm x)\\
\bound(\lrm x~\lrm y)\\
\bound(\lambda \lrm x.\lrm x~\lrm y)\\
\bound(\lambda \lrm x\lrm y.\lrm x~\lrm y)\\
\bound((\lambda\lrm x.\lrm y)~\lrm x)
\end{array}\tag*{$\square$}
\end{displaymath}
\end{aufgabe}
%
Der $\lambda$-Kalkül ist darauf
angewiesen, Variablen durch andere zu ersetzen, ohne dabei die
Zugehörigkeit von Variablenvorkommen und den dazu passenden
Abstraktionen zu verändern. Der Mechanismus dafür heißt auch hier
\textit{Substitution}:
%
\begin{definition}[Substitution]\index{Substitution}\label{def:substitution}
Für $e,f\in \mathcal{L}_{\lambda}$ bedeutet $e[v\mapsto f]$, dass in
$e$ die Variable $v$ durch $f$
ersetzt oder \textit{substituiert} wird. Die Funktion ist
folgendermaßen definiert:\index{*@$\mapsto$}
\begin{displaymath}
e[v\mapsto f] \deq
\begin{cases}
f & \text{falls } e = v\\
e & \text{falls } e \text{ eine Variable ist und } e \neq v\\
\lambda v.e_0 & \text{falls } e = \lambda v.e_0\\
\lambda u.(e_0[v\mapsto f]) & \text{falls } e = \lambda u.e_0 \text{
und } u\neq v, u\not\in\free(f)\\
\lambda u'.(e_0[u\mapsto u'][v\mapsto f]) & \text{falls }
e = \lambda u.e_0 \\ & \text{und }
u \neq v,u\in\free(f), u'\not\in\free(e_0)\cup\free(f)\\
(e_0[v\mapsto f])~(e_1[v\mapsto f]) &
\text{falls } e = e_0~e_1
\end{cases}
\end{displaymath}
\end{definition}
%
Die Definition der Substitution erscheint auf den ersten Blick
kompliziert, folgt aber dem Prinzip der
lexikalischen Bindung. Die erste Regel besagt, dass das Vorkommen
einer Variable durch eine Substitution genau dieser Variablen ersetzt
wird:
%
\begin{displaymath}
v[v\mapsto f] = f
\end{displaymath}
%
Die zweite Regel besagt, dass das Vorkommen einer \emph{anderen}
Variable durch die Substitution nicht betroffen wird:
%
\begin{displaymath}
e[v\mapsto f] = e \qquad e \text{ ist eine Variable und } e \neq v
\end{displaymath}
%
Die dritte Regel ist auf den ersten Blick etwas überraschend:
%
\begin{displaymath}
(\lambda v.e_0)[v\mapsto f] = \lambda v.e_0
\end{displaymath}
%
Ein $\lambda$-Ausdruck, dessen Parameter gerade die Variable ist, die
substitutiert werden soll, bleibt unverändert. Das liegt daran, dass
mit dem $\lambda$-Ausdruck die Zugehörigkeit aller Vorkommen von $v$
in $e_0$ bereits festgelegt ist: ein Vorkommen von $v$ in $e_0$ gehört
entweder zu dieser Abstraktion oder einer anderen Abstraktion mit $v$
als Parameter, die in $e_0$ weiter innen steht~-- $v$ ist in $(\lambda
v.e_0)$ gebunden und $v\in\bound(\lambda v.e_0)$.
Da die Substitution diese Zugehörigkeiten nicht verändern darf, lässt
sie das $v$ in Ruhe.
Anders sieht es aus, wenn die Variable der Abstraktion eine andere
ist~-- die vierte Regel:
%
\begin{displaymath}
(\lambda u.e_0)[v\mapsto f] = \lambda u.(e_0[v\mapsto f])
\qquad u\neq v, u\not\in\free(f)
\end{displaymath}
%
In diesem Fall wird die Substitution auf den Rumpf der Abstraktion
angewendet. Wichtig ist, dass $u$ nicht frei in $f$ vorkommt~-- sonst
könnte es vorkommen, dass beim Einsetzen von $f$ ein freies Vorkommen
von $u$ durch die umschließende Abstraktion gebunden wird.
Damit würde auch die Zugehörigkeitsregel der lexikalischen Bindung\index{lexikalische Bindung}
verletzt.
Was passiert, wenn $u$ eben doch frei in $f$ vorkommt, beschreibt die
fünfte Regel:
%
\begin{displaymath}
\begin{array}{lr}
(\lambda u.e_0)[v\mapsto f] = \lambda u'.(e_0[u\mapsto u'][v\rightarrow f])
& u\neq v, u\in\free(f)\\& u'\not\in\free(e_0)\cup\free(f)
\end{array}
\end{displaymath}
%
Um zu verhindern, dass die freien $u$ in $f$ durch die
Abstraktion "<eingefangen"> werden, wird das
$u$ in der Abstraktion durch ein
"<frisches"> $u'$ ersetzt, das noch nirgendwo frei vorkommt.
Die Regel für Substitution auf
Applikationen taucht in Operator und Operand
rekursiv ab:
%
\begin{displaymath}
(e_0~e_1)[v\mapsto f] = (e_0[v\mapsto f])(e_1[v\mapsto f])
\end{displaymath}
%
Hier ist ein etwas umfangreicheres Beispiel für die Substitution:
%
\begin{eqnarray*}
(\lambda \lrm x.\lambda \lrm y.\lrm x~(\lambda \lrm z.\lrm z)~\lrm z)[\lrm z \mapsto \lrm x~\lrm y]
&=&
\lambda \lrm x'.((\lambda \lrm y.\lrm x~(\lambda \lrm z.\lrm z)~\lrm z)[\lrm x\mapsto \lrm x'][\lrm z \mapsto \lrm x~\lrm y])
\\ &=&
\lambda \lrm x'.((\lambda \lrm y.((\lrm x~(\lambda \lrm z.\lrm z)~\lrm z)[\lrm x\mapsto \lrm x']))[\lrm z \mapsto \lrm x~\lrm y])
\\ &=&
\lambda \lrm x'.((\lambda \lrm y.(\lrm x[\lrm x\mapsto \lrm x']~((\lambda \lrm z.\lrm z)[\lrm x\mapsto \lrm x'])~\lrm z[\lrm x\mapsto \lrm x']))[\lrm z \mapsto \lrm x~\lrm y])
\\ &=&
\lambda \lrm x'.((\lambda \lrm y.(\lrm x'~(\lambda \lrm z.\lrm z)~\lrm z))[\lrm z \mapsto \lrm x~\lrm y])
\\ &=&
\lambda \lrm x'.\lambda \lrm y'.((\lrm x'~(\lambda \lrm z.\lrm z)~\lrm z)[\lrm y \mapsto \lrm y'][\lrm z \mapsto \lrm x~\lrm y])
\\ &=&
\lambda \lrm x'.\lambda \lrm y'.((\lrm x'[\lrm y \mapsto \lrm y']~((\lambda \lrm z.\lrm z)[\lrm y \mapsto \lrm y'])~\lrm z[\lrm y \mapsto \lrm y'])[\lrm z \mapsto \lrm x~\lrm y])
\\ &=&
\lambda \lrm x'.\lambda \lrm y'.((\lrm x'~(\lambda \lrm z.\lrm z)~\lrm z)[\lrm z \mapsto \lrm x~\lrm y])
\\ &=&
\lambda \lrm x'.\lambda \lrm y'.\lrm x'[\lrm z \mapsto \lrm x~\lrm y]~((\lambda \lrm z.\lrm z)[\lrm z \mapsto \lrm x~\lrm y])~\lrm z[\lrm z \mapsto \lrm x~\lrm y]
\\ &=&
\lambda \lrm x'.\lambda \lrm y'.\lrm x'~(\lambda \lrm z.\lrm z)~(\lrm x~\lrm y)
\end{eqnarray*}
%
Deutlich zu sehen ist, wie die freien Variablen $\lrm x$ und $\lrm y$ aus der
Substitution $\lrm z\mapsto \lrm x~\lrm y$ auch im Ergebnis frei bleiben, während die
gebundenen Variablen $\lrm x$ und $\lrm y$ aus dem ursprünglichen Term umbenannt
werden, um eine irrtümliche Bindung ihrer hereinsubstitutierten
Namensvettern zu vermeiden.
\section{Reduktion im $\lambda$-Kalkül}
Der $\lambda$-Kalkül ist ein sogenannter
\textit{Reduktionskalkül}.\index{Reduktionskalkül} Das heißt, er legt
Regeln fest, wie ein $\lambda$-Term in einen anderen $\lambda$-Term
überführt beziehungsweise \textit{reduziert} wird. ("<Reduzieren">
klingt so, als würde der Term dabei kleiner werden. Manchmal wird er
auch kleiner, manchmal aber auch größer.) Ein Beispiel für einen
Reduktionskalkül haben wir bereits gesehen, nämlich den Stepper im
DrRacket, der auch jeweils einen Ausdruck "<links"> in einen Ausdruck
"<rechts"> überführt. Hier sind die wichtigsten Reduktionsregeln im
$\lambda$-Kalkül, auch mit griechischen Buchstaben benannt:
%
\begin{definition}[Reduktionsregeln]\index{beta-Reduktion@$\beta$-Reduktion}\index{alpha-Reduktion@$\alpha$-Reduktion}\index{*@$\rightarrow_{\alpha}$}\index{*@$\rightarrow_{\beta}$}
Die Reduktionsregeln im $\lambda$"=Kalkül sind
die $\alpha$"=Reduktion $\rightarrow_{\alpha}$ und die
$\beta$-Reduktion $\rightarrow_{\beta}$:% und $\eta$:
%
\begin{alignat*}{2}
\lambda x.e & \rightarrow_{\alpha} \lambda y.(e[x\mapsto y]) \quad
& y\not\in\free(e)
\\
(\lambda v.e)~f & \rightarrow_{\beta} e[v\mapsto f]
% \\
% (\lambda x.e~x) & \rightarrow_{\eta} e \quad & x\not\in\free(e)
\end{alignat*}
%
\end{definition}
%
Die $\alpha$-Reduktion (oft auch \textit{$\alpha$-Konversion} genannt)
benennt eine gebundene Variable in eine andere um. Hier sind einige
Beispiele:
%
\begin{eqnarray*}
\lambda \lrm a.\lrm a & \rightarrow_{\alpha} & \lambda \lrm c.\lrm c\\
\lambda \lrm a.\lrm a~\lrm b & \rightarrow_{\alpha} & \lambda \lrm c.\lrm c~\lrm b\\
\lambda \lrm a.(\lambda \lrm a.\lrm a~\lrm a)~\lrm a & \rightarrow_{\alpha} &
\lambda \lrm c.(\lambda \lrm a'.\lrm a'~\lrm a')~\lrm c
\end{eqnarray*}
%
Die $\beta$-Reduktion ist die zentrale Regel des $\lambda$-Kalküls und steht für
Funktionsapplikation: eine Abstraktion
wird angewendet, indem die Vorkommen ihres Parameters durch den
Operanden einer Applikation ersetzt werden. Hier sind zwei
Beispiele:
%
\begin{eqnarray*}
(\lambda \lrm x.\lrm x)~\lrm y & \rightarrow_{\beta} & \lrm y\\
(\lambda \lrm a.\lrm a~\lrm a)~(\lambda \lrm x.\lrm x) & \rightarrow_{\beta}
& (\lambda \lrm x.\lrm x)~(\lambda \lrm x.\lrm x)
\end{eqnarray*}
%
In den obigen Beispielen wird jeweils die Reduktionsregel auf einen
ganzen $\lambda$-Termin angewendet. Es ist allerdings auch erlaubt,
eine Reduktionsregel auf einen Teilterm anzuwenden:
%
\begin{eqnarray*}
\lambda \lrm b.\lambda \lrm a.\lrm a & \rightarrow_{\alpha} & \lambda \lrm b.\lambda \lrm c.c\\
\lambda \lrm z.(\lambda \lrm x.\lrm x)~\lrm y & \rightarrow_{\beta}
& \lambda \lrm z.\lrm y
\end{eqnarray*}
%
Das ist bei jedem Reduktionskalkül so, gehört also zum Begriff
"<Reduktion"> an sich. Du kennst das Prinzip vom ganz normalen
Rechnen:
%
\begin{displaymath}
y \times (x + x + x) = y \times (3\times x)
\end{displaymath}
%
Der Stepper zeigt, dass auch in den Lehrsprachen Reduktion auf
Teiltermen arbeitet: Dieser wird ja farblich markiert. Allerdings
gibt es einen Unterschied: Beim Rechnen und im $\lambda$-Kalkül darf
auf \emph{jedem} Teilterm reduziert werden. Unter Umständen sind
mehrere verschiedene Reduktionen möglich:
%
\begin{eqnarray*}
(\lambda \lrm v .\lrm v~\lrm v)~\underline{((\lambda \lrm u.\lrm u)~\lrm w)}
& \rightarrow_{\beta} & (\lambda \lrm v .\lrm v~\lrm v)~\lrm w\\
\underline{(\lambda \lrm v .\lrm v~\lrm v)~((\lambda \lrm u.\lrm u)~\lrm w)}
& \rightarrow_{\beta} & ((\lambda \lrm u.\lrm u)~\lrm w)~((\lambda \lrm u.\lrm u)~\lrm w)
\end{eqnarray*}
%
Wir haben jeweils unterstrichen, welcher Teilterm reduziert wird:
Dieser Teilterm heißt \textit{Redex}.\index{Redex} Bei den
Lehrsprachen ist immer genau vorgeschrieben, welcher Teilterm der
Redex ist. Die Regeln dafür werden wir in
Abschnitt~\ref{sec:lambda-evaluation-strategies} auf
Seite~\pageref{sec:lambda-evaluation-strategies} kennenlernen.
% FIXME: Übungsaufgabe?
%\begin{comment}
%Die $\eta$-Regel ist eine Art vorweggenommene $\beta$-Reduktion: Ein
%Term $\lambda x.e~x$ hat die gleiche Wirkung als Operator einer
%Applikation wie $e$:
%%
%\begin{displaymath}
% (\lambda x.e~x)~f \rightarrow_\beta e
%\end{displaymath}
%%
%Wichtig ist dabei die Voraussetzung der $\eta$-Regel: $x$ darf nicht
%frei in $e$ vorkommen, was dafür sorgt, dass die Substitution von $x$
%durch $f$ in $e$ keinerlei Wirkung hat.
%\end{comment}
Wenn Du beim letzten Beispiel weiterreduzierst, dann sieht das so aus:
%
\begin{eqnarray*}
\underline{(\lambda \lrm v .\lrm v~\lrm v)~\lrm w} & \rightarrow_{\beta} & \lrm w~\lrm w\\
\underline{((\lambda \lrm u.\lrm u)~\lrm w)}~((\lambda \lrm u.\lrm u)~\lrm w)
& \rightarrow_{\beta} &
\lrm w~\underline{((\lambda \lrm u.\lrm u)~\lrm w)} \\
& \rightarrow_{\beta} & \lrm w~\lrm w
\end{eqnarray*}
%
\begin{aufgabeinline}
Kannst Du die bei diesem Beispiel auch noch auf einem anderen Weg
reduzieren, den Redex also bei mindestens einem anderen Schritt
anders wählen? Wenn ja, kommt auch dann das gleiche Ergebnis
heraus, wenn Du weiterreduzierst?
\end{aufgabeinline}
%
Wenn wir mehrere Schritte benötigen, um von einem Term zum anderen zu
kommen, werden wir die folgende Notation "<mit Sternchen"> benutzen:
%
\begin{eqnarray*}
e \overset{\ast}{\rightarrow_\alpha} e'\\
e \overset{\ast}{\rightarrow_\beta} e'\\
\end{eqnarray*}
%
Das bedeutet, dass wir in mehreren Reduktionsschritten (es können auch
% die "0" sieht im gerenderten PDF bescheiden aus... (wie ein "o")
0 Schritte sein, also $e = e'$) jeweils von $e$ nach $e'$ kommen.
Diese Konstruktion~-- "<0 oder mehr Schritte">~-- heißt auch
\textit{transitiv-reflexiver Abschluss}.\index{transitiv-reflexiver
Abschluss}\index{Abschluss!transitiv-reflexiv} Das "<reflexiv">
bedeutet, dass auch 0 Schritte möglich sind, das "<transitiv">, dass
es mehrere Schritte sein können.
Wir werden außerdem die Notation $\rightarrow_{\alpha,\beta}$
benutzen, um auszudrücken, dass es eine $\alpha$- oder eine
$\beta$-Reduktion ist. Entsprechend ist
$\overset{\ast}{\rightarrow_{\alpha,\beta}}$ eine Folge von $\alpha$-
und $\beta$-Reduktionen, die sich beliebig abwechseln können. Wir
setzen noch eins drauf: $\leftrightarrow_\beta$ bedeutet, dass die
Reduktion auch "<rückwärts"> laufen kann. Entsprechend gibt es auch
$\leftrightarrow_{\alpha, \beta}$ und
$\overset{\ast}{\leftrightarrow_{\alpha,\beta}}$.
$\leftrightarrow_{\alpha, \beta}$ heißt \textit{symmetrischer
Abschluss}\index{symmetrischer
Abschluss}\index{Abschluss!symmetrisch} von $\rightarrow_{\alpha,
\beta}$ ("<symmetrisch">, weil die Reduktion in beide Richtungen
gleich funktioniert), $\overset{\ast}{\leftrightarrow_{\alpha,\beta}}$
der \textit{reflexiv-transitiv-symmetrische Abschluss}\index{reflexiv-transitiv-symmetrischer Abschluss}\index{Abschluss!reflexiv-transitiv-symmetrisch}.
Wenn wir mit $\overset{\ast}{\leftrightarrow_{\alpha,\beta}}$ einen
$\lambda$-Term in einen anderen überführen können, so nennen wir diese
beiden Terme im $\lambda$-Kalkül
\textit{äquivalent}:
%
\begin{definition}[Äquivalenz im $\lambda$-Kalkül]
Zwei Terme $e_1, e_2\in\mathcal{L}_{\lambda}$ heißen
\textit{$\alpha\beta$-äquivalent} oder einfach nur
\textit{äquivalent}, wenn
%
\(e_1 \overset{\ast}{\leftrightarrow_{\alpha,\beta}} e_2\)
%
gilt.
Die Schreibweise dafür ist \(e_1\equiv e_2\).
\end{definition}
\section{Normalformen}
\label{sec:normalformen}
\index{Normalform}
Im letzten Abschnitt haben wir anhand des Beispiels
$
(\lambda \lrm v .\lrm v~\lrm v)~((\lambda \lrm u.\lrm u)~\lrm w)
$
%
gesehen, dass es von ein und demselben $\lambda$-Term aus mehrere
% wenn du jede ... jede was?
mögliche Reduktionsfolgen gibt. Wenn Du jede mit $\beta$-Reduktionen
bis zum Ende weiterführst, stellst Du fest, dass am Ende jedesmal der
gleiche Term herauskommt. Das heißt nicht ganz: Der Term hat am Ende
immer die gleiche Struktur, aber unter Umständen unterschiedliche
Namen für die Variablen. Du kannst aber mit Hilfe der
$\alpha$-Reduktion die Variablen so umbennen, dass auch sie gleich sind.
Um über diese Eigenschaft des $\lambda$-Kalküls zu sprechen, brauchen
wir den Begriff der \textit{Normalform}, der Terme beschreibt, die
nicht mehr weiter reduziert werden können:
%
\begin{definition}[Normalform]
Sei $e$ ein $\lambda$-Term. Ein $\lambda$-Term $e'$ ist eine
\textit{Normalform} von $e$, wenn
$e\overset{\ast}{\rightarrow_\beta}e'$ gilt und kein $\lambda$-Term
$e''$ existiert mit
$e'\rightarrow_\beta e''$.
\end{definition}
%
Normalformen können wir dazu benutzen, um den Beweis von Gleichungen
im Kalkül zu erleichtern: Wenn jemand behauptet, dass $e_1 \equiv e_2$
gilt, wissen wir ja nicht, welche Abfolge von $\alpha$- und
$\beta$-Reduktionen den einen in den anderen Term überführt hat. Es
könnte sein, dass wir viele Reduktionsfolgen probieren müssen, um eine
richtige zu finden.
Glücklicherweise ist der Beweis für $e_1 \equiv e_2$ einfacher, wenn
wir beide jeweils zu einer Normalform reduzieren können. Dann müssen
wir nur schauen, ob wir die Normalformen durch $\alpha$-Reduktionen
ineinander überführen können. Wenn ja, dann sind $e_1$ und $e_2$
äquivalent, sonst nicht. Für diese Situation werden auch die
Sprechweisen "<die Normalformen sind $\alpha$-äquivalent"> oder "<die
Normalformen sind gleich modulo $\alpha$-Reduktion">\index{modulo} benutzt.
Eine wichtige Eigenschaft auf dem Weg zur Eindeutigkeit von
Normalformen ist der Satz von Church/Rosser:
%
\begin{satz}[Church/Rosser-Eigenschaft]\index{Church/Rosser-Eigenschaft}
\label{satz:church-rosser}
Die $\beta$-Reduktionsregel hat die
\textit{Church/Rosser"=Eigenschaft}: Für
beliebige $\lambda$-Terme $e_1$ und $e_2$ mit
$e_1 \overset{\ast}{\leftrightarrow_\beta} e_2$,
gibt es immer einen $\lambda$-Term $e'$ mit
$e_1\overset{\ast}{\rightarrow_\beta} e'$ und
$e_2\overset{\ast}{\rightarrow_\beta} e'$.
\end{satz}
%
\begin{figure}[htb]
\begin{center}
\begin{tikzpicture}
\node(e1) at (0,1) {$e_1$};
\node(e2) at (2,1) {$e_2$};
\node(eprime) at (1,0) {$e'$};
\draw[<->] (e1) -- (e2) node[midway,above]{$\overset{\ast}{\rightarrow_\beta}$};
\draw[->] (e1) -- (eprime) node[midway,left]{$\overset{\ast}{\rightarrow_\beta}$};
\draw[->] (e2) -- (eprime) node[midway,right]{$\overset{\ast}{\rightarrow_\beta}$};
\end{tikzpicture}
\caption{Die Church/Rosser-Eigenschaft}
\label{fig:church-rosser}
\end{center}
\end{figure}
Abbildung~\ref{fig:church-rosser} stellt die Aussage des Satzes von
Church/Rosser grafisch dar.
Der Beweis des Satzes ist leider recht umfangreich und technisch.
Die einschlägige Literatur über den $\lambda$-Kalkül hat ihn
vorrätig~\cite{HindleySeldin1986}.
Die Church/Rosser-Eigenschaft ebnet den Weg für Benutzung von
Normalformen zum Finden von Beweisen im $\lambda$-Kalkül:
%
\begin{satz}[Eindeutigkeit der Normalform]
\label{satz:normalform}
Ein $\lambda$-Term $e$ hat höchstens eine Normalform modulo
$\alpha$-Reduktion.
\end{satz}
%
\begin{beweis}
Angenommen, es gebe zwei unterschiedliche Normalformen $e_1$ und
$e_2$ von $e$. Nach Satz~\ref{satz:church-rosser} muss es dann
aber einen weiteren $\lambda$-Term $e'$ geben mit $e_1\overset{\ast}{\rightarrow_\beta} e'$ und
$e_2\overset{\ast}{\rightarrow_\beta} e'$. Entweder sind $e_1$ und
$e_2$ also nicht unterschiedlich, oder zumindest einer von
beiden ist keine Normalform im Widerspruch zur Annahme.
\end{beweis}
%
Satz~\ref{satz:normalform} bestätigt, dass
der $\lambda$-Kalkül ein sinnvoller Mechanismus für die Beschreibung
des Verhaltens von Computerprogrammen ist: Bei einem $\lambda$-Term
ist es gleichgültig, in welcher Reihenfolge die Reduktionen
angewendet werden: Jede Reduktionsfolge, die zu einer Normalform
führt, führt immer zur gleichen Normalform.
Manche
$\lambda$-Terme haben leider keine Normalform. Hier ein
Beispiel:
%
\begin{displaymath}
(\lambda \lrm x.\lrm x~\lrm x)(\lambda \lrm x.\lrm x~\lrm x) \rightarrow_\beta (\lambda \lrm x.\lrm x~\lrm x)~(\lambda x.\lrm x~\lrm x)
\end{displaymath}
%
Solche Terme ohne Normalformen lassen sich endlos weiterreduzieren,
ohne dass der Prozess jemals zum Schluss kommt. Sie entsprechen damit
Programmen, die endlos weiterrechnen.
Dies ist kein spezieller Defekt des
$\lambda$-Kalküls: Jeder Kalkül, der mächtig genug ist, um beliebige
Computerprogramme zu modellieren, hat diese Eigenschaft.
\section{Auswertungsstrategien}
\label{sec:lambda-evaluation-strategies}
\index{Auswertungsstrategie}Die Anwendung des Satzes von
Church/Rosser hat in der Praxis einen Haken: Er besagt zwar, dass sich
die Äquivalenz von zwei Termen dadurch beweisen lässt, dass ihre
Normalformen verglichen werden. Leider sagt er nichts darüber,
wie diese Normalformen gefunden werden. Wenn wir die Auswertung einem
Computer übertragen, sollten wir uns eine Strategie überlegen, die
möglichst effizient zur Normalform führt und möglichst keine
überflüssigen Auswertungsschritte begeht.
Eine solche \textit{Auswertungsstrategie} wird in der Regel so
formuliert, dass sie ausgehend von einem $\lambda$-Term den
$\beta$-Redex für den nächsten Auswertungsschritt eindeutig festlegt.
Für den $\lambda$-Kalkül gibt es mehrere
populäre Auswertungsstrategien, die jeweils ihre eigenen Vor- und
Nachteile haben, was das effektive Finden von Normalformen betrifft.
Eine populäre Auswertungsstrategie ist die Linksaußen-Reduktion\index{Linksaußen-Reduktion}, auch
\textit{normal-order reduction\index{normal-order reduction}} oder
\textit{leftmost-outermost reduction\index{leftmost-outermost reduction}} genannt.
%
\begin{definition}[Linksaußen-Reduktion]
Bei der Linksaußen-Reduktion wird immer der $\beta$-Redex reduziert,
der möglichst weit links außen steht. Das bedeutet konkret:
%
\begin{enumerate}
\item Wenn der $\lambda$-Term die Form $(\lambda v.e)~f$ hat, so
ist der ganze Term der nächste Redex~-- also \emph{außen}.
\item Wenn der $\lambda$-Term die Form $e~f$ hat, und $e$
\emph{keine} Abstraktion ist, so suche nach dem Redex innerhalb
von $e$~-- also \emph{links}.
\item Wenn der $\lambda$-Term die Form $e~f$ hat, und $e$
\emph{keine} Abstraktion ist und wenn in $e$ kein Redex zu finden
ist, suche nach dem Redex innerhalb von $f$.
\item Wenn der $\lambda$-Term die Form $\lambda v.e$ hat, so suche
nach dem Redex innerhalb von $e$.
\end{enumerate}
\end{definition}
%
Hier ist ein Beispiel:
%
\begin{eqnarray*}
\underline{(\lambda \lrm x.\lambda \lrm z.(\lambda \lrm y.\lrm y)~\lrm x)~((\lambda \lrm y.\lrm y)~\lrm z)}
& \rightarrow_\beta &
\lambda \lrm z.\underline{(\lambda \lrm y.\lrm y)~((\lambda \lrm y.\lrm y)~\lrm z)}\\
& \rightarrow_\beta & \lambda \lrm z.\underline{(\lambda \lrm y.\lrm y)~\lrm z}\\
& \rightarrow_\beta & \lambda \lrm z.\lrm z
\end{eqnarray*}
%
Die Linksaußen-Reduktion hat folgende äußerst angenehme Eigenschaft:
%
\begin{satz}
Wenn $e'$ eine Normalform von $e$ ist, so führt die
Linksaußen-Reduktion von $e$ zu $e'$, modulo $\alpha$-Reduktion.
\end{satz}
%
Falls es also eine Normalform gibt, so findet die Linksaußen-Reduktion
sie auch.
Es gibt noch weitere Auswertungsstrategien:
%
\begin{definition}[Call-by-Name-Reduktion]
Die \textit{Call-by-Name-Reduktion} ist wie die
Linksaußen-Reduktion, allerdings ohne die letzte Regel für Terme der
Form $\lambda v.e$.
\end{definition}
%
Die Call-by-Name-Reduktion hört also verglichen mit der
Linksaußen-Reduktion vorzeitig auf, sobald eine Abstraktion
herauskommt. Beim Beispiel von oben ist schon nach dem
ersten Schritt:
%
\begin{eqnarray*}
\underline{(\lambda \lrm x.\lambda \lrm z.(\lambda \lrm y.\lrm y)~\lrm x)~((\lambda \lrm y.\lrm y)~\lrm z)}
& \rightarrow_\beta &
\lambda \lrm z.(\lambda \lrm y.\lrm y)~((\lambda \lrm y.\lrm y)~\lrm z)
\end{eqnarray*}
%
Dass die Linksaußen-Reduktion immer eine Normalform findet, wenn es
eine gibt, ist toll. Leider geschieht dies nicht immer auf die
effizienteste Art und Weise.
Beim folgenden Beispiel wird der Subterm $((\lambda \lrm y.\lrm y)~\lrm z)$
zunächst "<verdoppelt"> und muss deshalb zweimal reduziert
werden.
%
\begin{displaymath}
\begin{array}{rl}
\underline{(\lambda \lrm x.\lrm x~\lrm x)~((\lambda \lrm y.\lrm y)~\lrm z)}
\rightarrow_{\beta} &
\underline{((\lambda \lrm y.\lrm y)~\lrm z)~((\lambda \lrm y.\lrm y)~\lrm z)}
\\
\rightarrow_{\beta} &
\lrm z~\underline{((\lambda \lrm y.\lrm y)~\lrm z)}
\\
\rightarrow_{\beta} &
\lrm z~\lrm z
\end{array}
\end{displaymath}
%
Eine andere Auswertungsstrategie
vermeidet solche doppelte Arbeit: Die \textit{Linksinnen"=Reduktion},
auch genannt \emph{applicative-order reduction\index{applicative-order reduction}} oder