eXamen.press eXamen.press ist eine Reihe, die Theorie und Praxis aus allen Bereichen der Informatik für die Hochschulausbildung vermittelt. Peter Pepper Petra Hofstedt Funktionale Programmierung Sprachdesign und Programmiertechnik Mit 57 Abbildungen und 18 Tabellen 123 Peter Pepper Petra Hofstedt Technische Universität Berlin Fakultät IV – Elektrotechnik und Informatik Institut für Softwaretechnik und Theoretische Informatik Franklinstraße 28/29 10587 Berlin [email protected] [email protected] Bibliografische Information der Deutschen Bibliothek Die Deutsche Bibliothek verzeichnet diese Publikation in der Deutschen Nationalbibliografie; detaillierte bibliografische Daten sind im Internet über http://dnb.ddb.de abrufbar. ISSN 1614-5216 ISBN-10 3-540-20959-X Springer Berlin Heidelberg New York ISBN-13 978-3-540-20959-1 Springer Berlin Heidelberg New York Dieses Werk ist urheberrechtlich geschützt. Die dadurch begründeten Rechte, insbesondere die der Übersetzung, des Nachdrucks, des Vortrags, der Entnahme von Abbildungen und Tabellen, der Funksendung, der Mikroverfilmung oder der Vervielfältigung auf anderen Wegen und der Speicherung in Datenverarbeitungsanlagen, bleiben, auch bei nur auszugsweiser Verwertung, vorbehalten. Eine Vervielfältigung dieses Werkes oder von Teilen dieses Werkes ist auch im Einzelfall nur in den Grenzen der gesetzlichen Bestimmungen des Urheberrechtsgesetzes der Bundesrepublik Deutschland vom 9. September 1965 in der jeweils geltenden Fassung zulässig. Sie ist grundsätzlich vergütungspflichtig. Zuwiderhandlungen unterliegen den Strafbestimmungen des Urheberrechtsgesetzes. Springer ist ein Unternehmen von Springer Science+Business Media springer.de © Springer-Verlag Berlin Heidelberg 2006 Printed in Germany Die Wiedergabe von Gebrauchsnamen, Handelsnamen, Warenbezeichnungen usw. in diesem Werk berechtigt auch ohne besondere Kennzeichnung nicht zu der Annahme, dass solche Namen im Sinne der Warenzeichen- und Markenschutz-Gesetzgebung als frei zu betrachten wären und daher von jedermann benutzt werden dürften. Text und Abbildungen wurden mit größter Sorgfalt erarbeitet. Verlag und Autor können jedoch für eventuell verbliebene fehlerhafte Angaben und deren Folgen weder eine juristische Verantwortung noch irgendeine Haftung übernehmen. Satz: Druckfertige Daten der Autoren Herstellung: LE-TEX, Jelonek, Schmidt & Vöckler GbR, Leipzig Umschlaggestaltung: KünkelLopka Werbeagentur, Heidelberg Gedruckt auf säurefreiem Papier 33/3142 YL – 5 4 3 2 1 0 Für Claudia Meinen Eltern Christel und Klaus Hofstedt Vorwort Lernen ist wie Rudern gegen den Strom; sobald man damit aufhört, treibt man zurück. Chinesische Weisheit Auch wenn es sich bei diesem Text in gewissem Sinn um die Fortführung des einführenden Lehrbuchs [111], so ist sein Ziel doch ein ganz anderes. In dem Vorgängerbuch [111] geht es vor allem darum, dem Anfänger einen Einstieg in das konkrete Programmieren mit existierenden Funktionalen Sprachen zu geben. Dafür werden drei Sprachen benutzt: opal, ml und haskell. Das vorliegende Buch wendet sich an fortgeschrittene Leser, die mehr über Funktionale Sprachen – und nicht nur diese – lernen wollen. Und das betrifft nicht nur real existierende Sprachen, sondern – was noch wichtiger ist – Konzepte, die heute untersucht, diskutiert und ausgearbeitet und in der einen oder anderen Weise ihren Weg in künftige Sprachen finden werden. Der Weg, den wir dazu wählen, ist, eine fiktive Sprache zu behandeln, die sich zwar sehr stark an bekannten Sprachen – vor allem wieder an opal, ml und haskell – orientiert, aber in entscheidenden Punkten über sie hinaus geht und neue Ideen realisiert. Der Nachteil ist, dass es keinen Compiler gibt, den man aus dem Internet herunterladen könnte, um alles, was hier beschrieben ist, auszuprobieren. Dafür kann man aber verschiedene Konzepte nebeneinander sehen, die sich sonst nur in getrennten Sprachen jeweils für sich alleine studieren lassen. Darüber hinaus sieht man Konzepte im Kontext einer Gesamtsprache, die sonst nur in losgelösten wissenschaftlichen Papieren zu betrachten sind. Aber natürlich wäre es sehr unbefriedigend, wenn es sich bei den von uns diskutierten Themen nur um akademische Studien handeln würde. Vieles von dem, was hier an Konzepten vorgestellt wird, ist in dieser Form Gegenstand von experimentellen Implementierungen unserer Arbeitsgruppe an der Technischen Universität Berlin. Und es ist nicht ausgeschlossen, dass daraus eines Tages auch eine reale Sprache opal-2 wird, mit einem Compiler, den man aus dem Internet herunterladen kann . . . VIII Vorwort Ein solches Unterfangen wird nicht alleine von zwei Menschen geleistet. Es tragen im Laufe der Jahre viele dazu bei. An erster Stelle sind hier die ehemaligen und aktuellen Mitarbeiter unserer Arbeitsgruppe an der TU Berlin zu nennen, insbesondere (in alphabetischer Reihenfolge) Michael Cebulla, Klaus Didrich, Thomas Frauenstein, Wolfgang Grieskamp, Markus Lepper, Christian Maeder, Thomas Nitsche, Wolfram Schulte, Mario Südholt, Baltasar Trancon-y-Wideman, Stephan Weber und Jacob Wieland. Sie haben im Rahmen von Projekten, Doktorarbeiten und Lehrveranstaltungen viel zur Gestaltung der hier diskutierten Konzepte beigetragen. Unserer besonderer Dank gilt André Metzner für viele fruchtbare Diskussionen, sowie Dirk Reckmann und Martin Grabmüller, die Teile des Buches kritisch gelesen und durch vielfältige Kommentare verbessert haben. Vor allem aber schulden wir Stephan Frank großen Dank, der sowohl inhaltlich als auch technisch einen enormen Beitrag geleistet hat. Wir hatten aber auch das Vergnügen, mit vielen Kollegen innerhalb und außerhalb der TU Berlin zahlreiche stimulierende Diskussionen zu führen, aus denen wichtige Anregungen für dieses Buch hervorgingen. Besonders hervorzuheben sind hier Bernd Mahr von der TU Berlin sowie Doug Smith und Dusko Pavlovic vom Kestrel Institute. Auch die Diskussionen mit den Kollegen der IFIP Working Groups 2.1 und 1.3 haben uns viele Einsichten gebracht. Die Mitarbeiter des Springer-Verlags haben durch ihre kompetente Unterstützung viel zu der jetzigen Gestalt des Buches beigetragen. Berlin, im März 2006 Peter Pepper Petra Hofstedt Inhaltsverzeichnis Teil I Elementare Funktionale Programmierung Eine Wiederholung 0 Das 0.1 0.2 0.3 0.4 0.5 Strittigste vorab: Notationen . . . . . . . . . . . . . . . . . . . . . . . . . . Jenseits von ASCII . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Jenseits von Infix: Mixfix . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Overloading extrem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Layout mit Bedeutung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Bindung von Variablen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 4 5 6 7 8 1 Grundlagen der Funktionalen Programmierung . . . . . . . . . . . . 1.1 Funktionen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.1.1 Funktionsdefinition . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.1.2 Ausdrücke . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.1.3 λ-Notation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.1.4 Funktionalitäten: Die Typisierung von Funktionen . . . . . 1.1.5 Partielle Applikation und Currying . . . . . . . . . . . . . . . . . . 1.1.6 Typen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.1.7 Musterbasierte Funktionsdefinition . . . . . . . . . . . . . . . . . . 1.1.8 Erweitertes Patternmatching . . . . . . . . . . . . . . . . . . . . . . . . 1.1.9 Polymorphie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.1.10 Eigenschaften (Propertys) . . . . . . . . . . . . . . . . . . . . . . . . . . 1.1.11 Sequenzen (Listen, Folgen) . . . . . . . . . . . . . . . . . . . . . . . . . 1.2 Funktionale . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.2.1 Allgemeine Funktionale . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.2.2 Catamorphismen (Map-Filter-Reduce) . . . . . . . . . . . . . . . 1.3 Semantik und Auswertungsstrategien . . . . . . . . . . . . . . . . . . . . . . 1.3.1 Denotationelle Semantik . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.2 Operationale Semantik . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.4 Mit Programmen rechnen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.4.1 Von linearer Rekursion zu Tail-Rekursion . . . . . . . . . . . . . 11 11 12 13 13 15 16 17 18 19 20 21 22 23 24 25 31 31 32 35 35 X Inhaltsverzeichnis 1.4.2 Ein „universeller Trick“: Continuations . . . . . . . . . . . . . . . 38 1.4.3 Vereinfachung komplexerer Rekursionen . . . . . . . . . . . . . . 41 1.5 OPAL, ML und HASKELL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44 2 Faulheit währt unendlich . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.1 Unendliche Objekte: die Idee . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.2 lazy, quote und unquote . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.2.1 lazy als generischer Typ . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.2.2 Simulation in strikten Sprachen wie OPAL oder ML . . . . 2.3 lazy Listen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.4 Programmieren mit lazy Listen . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.4.1 Unbeschränkte Folgen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.4.2 Approximationsaufgaben . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.4.3 Animation („Ströme“) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47 47 49 50 50 51 53 53 55 56 3 Parser als Funktionen höherer Ordnung . . . . . . . . . . . . . . . . . . . 3.1 Vorbemerkung zu Grammatiken und Syntaxbäumen . . . . . . . . . 3.2 Parser . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3.3 Scanner . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3.4 Verallgemeinerungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59 61 62 65 67 Teil II Strukturierung von Programmen 4 Gruppen: Die Basis der Modularisierung . . . . . . . . . . . . . . . . . . 4.1 Items . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.2 Das allgemeine Konzept der Gruppen . . . . . . . . . . . . . . . . . . . . . . 4.2.1 Syntactic sugar: Schlüsselwörter . . . . . . . . . . . . . . . . . . . . . 4.2.2 Selektoren und die Semantik von Gruppen . . . . . . . . . . . . 4.3 Environments und Namensräume . . . . . . . . . . . . . . . . . . . . . . . . . . 4.3.1 Environments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.3.2 Scopes und lokale Namensräume . . . . . . . . . . . . . . . . . . . . 4.3.3 Namenserkennung im Scope . . . . . . . . . . . . . . . . . . . . . . . . 4.3.4 Namenserkennung außerhalb des Scopes (use) . . . . . . . . 4.4 Overloading . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.5 Beispiele für Strukturen und Packages . . . . . . . . . . . . . . . . . . . . . 4.6 Weitere syntaktische Spielereien . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.6.1 Verteilte Definition von Items . . . . . . . . . . . . . . . . . . . . . . . 4.6.2 Tupel als spezielle Gruppen . . . . . . . . . . . . . . . . . . . . . . . . . 4.7 Programme und das Betriebssystem . . . . . . . . . . . . . . . . . . . . . . . 4.7.1 Was ist eigentlich ein Programm? . . . . . . . . . . . . . . . . . . . 4.7.2 . . . und was ist mit den Programmdateien? . . . . . . . . . . . 73 74 74 76 77 79 81 81 82 83 85 86 88 88 90 90 91 92 Inhaltsverzeichnis 5 XI Operatoren auf Gruppen (Morphismen) . . . . . . . . . . . . . . . . . . . 95 5.1 Vererbung (extend) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95 5.2 Signatur-Morphismen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96 5.2.1 Restriktion (only, without) . . . . . . . . . . . . . . . . . . . . . . . 97 5.2.2 Renaming (renaming) . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97 5.2.3 Kombination und Verwendung von Morphismen . . . . . . . 98 5.2.4 Vererbung mit Modifikation . . . . . . . . . . . . . . . . . . . . . . . . 99 5.3 Geheimniskrämerei: Import und Export . . . . . . . . . . . . . . . . . . . . 101 5.3.1 Schutzwall nach außen – Export (private, public) . . . . 102 5.3.2 Schutzwall nach innen – Import . . . . . . . . . . . . . . . . . . . . . 103 5.4 Generizität: Funktionen, die Gruppen liefern . . . . . . . . . . . . . . . . 105 Teil III Die Idee der Typisierung 6 Typen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109 6.1 Generelle Aspekte von Typen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109 6.1.1 Die Pragmatik: Statische oder dynamische Typprüfung? 109 6.1.2 Reflection: Typen als „First-class citizens“ . . . . . . . . . . . 111 6.1.3 Intensionalität, Reflection und der Compiler . . . . . . . . . . 113 6.1.4 Typen sind auch nur Terme . . . . . . . . . . . . . . . . . . . . . . . . . 114 6.1.5 Typdeklarationen: Typsynonyme oder neue Typen? . . . . 114 6.1.6 Kinding: Typen höherer Stufe . . . . . . . . . . . . . . . . . . . . . . . 115 6.1.7 Mehrfachtypisierung (Mehrfachvererbung) . . . . . . . . . . . . 116 6.2 Elementare Typen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 116 6.3 Aufzählungstypen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117 6.4 Tupel- und Gruppentyp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119 6.4.1 Tupeltyp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119 6.4.2 Gruppentyp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 121 6.5 Summentypen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123 6.5.1 Was für ein Typ bist du? Dieser Typ! . . . . . . . . . . . . . . . . 124 6.5.2 Disjunkt oder nicht? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 126 6.5.3 Summen mit Typausdrücken . . . . . . . . . . . . . . . . . . . . . . . . 127 6.5.4 Syntactic sugar: Overloading von "‘ : "’ . . . . . . . . . . . . . . . 128 6.6 Funktionstypen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 128 6.7 Rekursive Typen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 129 6.8 Wie geht’s weiter? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 130 7 Subtypen (Vererbung) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131 7.1 Ein genereller Rahmen für Subtypen . . . . . . . . . . . . . . . . . . . . . . . 131 7.1.1 Die Subtyp-Relation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132 7.1.2 Typanpassung (Casting) . . . . . . . . . . . . . . . . . . . . . . . . . . . 133 7.1.3 Coercion Semantics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 134 7.2 Direkte Subtypen: Constraints . . . . . . . . . . . . . . . . . . . . . . . . . . . . 135 7.3 Gruppen/Tupel und Subtypen . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137 XII Inhaltsverzeichnis 7.3.1 Subtypen von Tupeltypen . . . . . . . . . . . . . . . . . . . . . . . . . . 139 7.3.2 Produkttypen mit Constraints . . . . . . . . . . . . . . . . . . . . . . 139 7.4 Summentypen und Subtypen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140 7.4.1 Varianten und „echte“ Subtypen . . . . . . . . . . . . . . . . . . . . 141 7.4.2 Summen + Tupel sind ein Schutzwall . . . . . . . . . . . . . . . . 142 7.5 Funktionstypen und Subtypen . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143 8 Polymorphe und abhängige Typen . . . . . . . . . . . . . . . . . . . . . . . . . 145 8.1 Typfunktionen (generische Typen, Polymorphie) . . . . . . . . . . . . . 146 8.1.1 Typvariablen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 146 8.1.2 Typfunktionen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 148 8.2 Abhängige Typen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 150 8.3 Notationen für Typterme mit Variablen . . . . . . . . . . . . . . . . . . . . 154 8.3.1 Bindung von Variablen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 154 8.3.2 Syntactic sugar: Nachgestellte Variablen . . . . . . . . . . . . . 155 8.3.3 Syntactic sugar: Optionale Parameter . . . . . . . . . . . . . . . . 155 9 Spezifikationen und Typklassen . . . . . . . . . . . . . . . . . . . . . . . . . . . . 157 9.1 Operatoren auf Typklassen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161 9.2 Signaturen: Die Typisierung von Strukturen . . . . . . . . . . . . . . . . 162 9.2.1 Syntactic sugar: Traditionelle Signatur-Notation . . . . . . . 163 9.2.2 Syntactic sugar: Verschmelzen von Struktur und Signatur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 164 9.3 Spezifikationen: Subtypen von Signaturen . . . . . . . . . . . . . . . . . . 165 9.4 Signaturen und Spezifikationen sind existenziell . . . . . . . . . . . . . 167 9.4.1 Zahlen generell betrachtet . . . . . . . . . . . . . . . . . . . . . . . . . . 167 9.4.2 Existenzielle Typen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 168 9.5 Von Spezifikationen zu Typklassen . . . . . . . . . . . . . . . . . . . . . . . . . 169 9.5.1 Typklassen à la HASKELL . . . . . . . . . . . . . . . . . . . . . . . . . . 169 9.5.2 Definition von Typklassen . . . . . . . . . . . . . . . . . . . . . . . . . . 170 9.6 Beispiele für Spezifikationen und ihre Typklassen . . . . . . . . . . . . 173 9.6.1 Gleichheit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 173 9.6.2 Ordnung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 174 9.6.3 Halbgruppen, Monoide and all that . . . . . . . . . . . . . . . . . . 175 9.6.4 Druckbares, Speicherbares . . . . . . . . . . . . . . . . . . . . . . . . . . 176 9.7 Subklassen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 177 9.8 Views und Mehrfachtypisierung . . . . . . . . . . . . . . . . . . . . . . . . . . . 178 9.8.1 Mehrfachtypisierung bei Typklassen . . . . . . . . . . . . . . . . . 178 9.8.2 Views (Mehrfache Sichten) . . . . . . . . . . . . . . . . . . . . . . . . . 179 9.9 Beispiel: Physikalische Dimensionen . . . . . . . . . . . . . . . . . . . . . . . . 184 10 Beispiel: Berechnung von Fixpunkten . . . . . . . . . . . . . . . . . . . . . . 187 10.1 Beispiel: Erreichbarkeit in einem Graphen . . . . . . . . . . . . . . . . . . 187 10.2 Ein bisschen Mathematik: CPOs und Fixpunkte . . . . . . . . . . . . . 189 10.2.1 Vollständige partielle Ordnungen – CPOs . . . . . . . . . . . . . 189 Inhaltsverzeichnis XIII 10.2.2 Standardkonstruktionen für CPOs . . . . . . . . . . . . . . . . . . . 190 10.2.3 CPO-Konstruktion durch Idealvervollständigung . . . . . . 191 10.2.4 Fixpunkte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 192 10.3 Die Programmierung von Fixpunkt-Algorithmen . . . . . . . . . . . . . 197 10.3.1 CPOs als Typklasse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 197 10.3.2 Fixpunktberechnung: Der Basisalgorithmus . . . . . . . . . . . 198 10.3.3 Optimierung durch feinere Granularität . . . . . . . . . . . . . . 199 10.3.4 Verallgemeinerungen und Variationen . . . . . . . . . . . . . . . . 201 10.3.5 Fixpunkte als „Design Pattern“ . . . . . . . . . . . . . . . . . . . . . 202 10.4 Datentypen als CPOs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 203 10.5 Beispiel: Lösung von Gleichungssystemen . . . . . . . . . . . . . . . . . . . 209 10.5.1 Repräsentation von Gleichungssystemen . . . . . . . . . . . . . . 210 10.5.2 Optimierung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 212 10.5.3 Nochmals: Grammatiken und Parser . . . . . . . . . . . . . . . . . 212 10.5.4 Ein Gedankenexperiment: Anpassbare Rekursion . . . . . . 215 11 Beispiel: Monaden . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 217 11.1 Kategorien, Funktoren und Monaden . . . . . . . . . . . . . . . . . . . . . . . 218 11.1.1 Typklassen als Kategorien . . . . . . . . . . . . . . . . . . . . . . . . . . 218 11.1.2 Polymorphe Typen als Funktoren . . . . . . . . . . . . . . . . . . . 219 11.1.3 Monaden . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 220 11.2 Beispiele für Monaden . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 222 11.2.1 Sequenzen als Monaden . . . . . . . . . . . . . . . . . . . . . . . . . . . . 222 11.2.2 Maybe als Monade . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 223 11.2.3 Automaten als Monaden („Zustands-Monaden“) . . . . . . . 224 11.2.4 Spezielle Zustands-Monaden . . . . . . . . . . . . . . . . . . . . . . . . 227 11.2.5 Zähler als Zustands-Monaden . . . . . . . . . . . . . . . . . . . . . . . 230 11.2.6 Generatoren als Zustands-Monaden . . . . . . . . . . . . . . . . . . 231 11.2.7 Ein-/Ausgabe als Zustands-Monade . . . . . . . . . . . . . . . . . . 231 11.3 Spezielle Notationen für Monaden . . . . . . . . . . . . . . . . . . . . . . . . . 231 11.3.1 Die Operatoren „→“ und „ ; “ . . . . . . . . . . . . . . . . . . . . . . . 232 11.3.2 Erweitertes let . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 232 11.3.3 Monaden-Casting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 233 Teil IV Datenstrukturen 12 Netter Stack und böse Queue . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 239 12.1 Wenn Listen nur anders heißen: Stack . . . . . . . . . . . . . . . . . . . . . . 241 12.2 Wenn Listen zum Problem werden: Queue . . . . . . . . . . . . . . . . . . 242 12.2.1 Variante 1: Queue = Paar von Listen . . . . . . . . . . . . . . . . 243 12.2.2 Variante 2: Faulheit macht kalkulierbar . . . . . . . . . . . . . . 245 12.3 Deque und Sequence . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 249 12.3.1 Double-ended Queues (Deque) . . . . . . . . . . . . . . . . . . . . . . 249 12.3.2 Sequenzen (Catenable Lists) . . . . . . . . . . . . . . . . . . . . . . . . 250 XIV Inhaltsverzeichnis 12.4 Arbeiten mit listenartigen Strukturen . . . . . . . . . . . . . . . . . . . . . . 252 13 Compilertechniken für funktionale Datenstrukturen . . . . . . . 255 13.1 Die Bedeutung von Single-Threadedness . . . . . . . . . . . . . . . . . . . . 256 13.1.1 Die Analyse von Single-Threadedness . . . . . . . . . . . . . . . . 257 13.1.2 Monaden garantieren Single-Threadedness . . . . . . . . . . . . 259 13.1.3 Lineare Typen garantieren Single-Threadedness . . . . . . . 261 13.2 A Dag For All Heaps (Reference-Counting) . . . . . . . . . . . . . . . . . 263 13.2.1 Ein Dag-Modell für das Datenmanagement . . . . . . . . . . . 263 13.2.2 Sicheres Management persistenter Strukturen . . . . . . . . . 268 13.2.3 Dynamische Erkennung von Single-Threadedness . . . . . . 273 13.2.4 „Tail-Rekursion modulo cons“ . . . . . . . . . . . . . . . . . . . . . . . 275 13.2.5 Reference-Counting und Queues: Hilft Laziness? . . . . . . . 280 13.3 Version Arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 283 14 Funktionale Arrays und Numerische Mathematik . . . . . . . . . . 287 14.1 Semantik von Arrays: Funktionen . . . . . . . . . . . . . . . . . . . . . . . . . . 288 14.1.1 Die Typklasse der Intervalle . . . . . . . . . . . . . . . . . . . . . . . . 288 14.1.2 Eindimensionale Arrays (Vektoren) . . . . . . . . . . . . . . . . . . 290 14.1.3 Mehrdimensionale Arrays (Matrizen) . . . . . . . . . . . . . . . . 293 14.1.4 Matrizen von besonderer Gestalt (Shapes) . . . . . . . . . . . . 294 14.1.5 Map-Reduce auf Arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . 296 14.2 Pragmatik von Arrays: „Eingefrorene“ Funktionen . . . . . . . . . . . 297 14.2.1 Memoization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 298 14.2.2 Speicherblöcke . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 300 14.2.3 Sicherheit und Single-Threadedness: Version Arrays . . . . 301 14.2.4 Von Arrays zu Speicherblöcken . . . . . . . . . . . . . . . . . . . . . . 301 14.2.5 Implementierung eindimensionaler Arrays . . . . . . . . . . . . 302 14.2.6 Implementierung mehrdimensionaler Arrays . . . . . . . . . . 303 14.3 Arrays in der Numerik: Vektoren und Matrizen . . . . . . . . . . . . . . 304 14.3.1 Vektoren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 304 14.3.2 Matrizen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 306 14.4 Beispiel: Gauß-Elimination . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 306 14.4.1 Lösung von Dreieckssystemen . . . . . . . . . . . . . . . . . . . . . . . 307 14.4.2 LU -Zerlegung (Doolittle-Variante) . . . . . . . . . . . . . . . . . . . 309 14.4.3 Spezialfall: Gauß-Elimination bei Tridiagonalmatrizen . . 311 14.5 Beispiel: Interpolation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 311 14.6 Beispiel: Spline-Interpolation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 314 14.7 Beispiel: Schnelle Fourier-Transformation (FFT) . . . . . . . . . . . . . 318 15 Map: Wenn Funktionen zu Daten werden . . . . . . . . . . . . . . . . . . 323 15.1 Variationen über Funktionen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 324 15.2 Die Typklasse der Funktionen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 325 15.3 Die Typklasse der Mappings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 326 15.3.1 Implementierungen von Maps . . . . . . . . . . . . . . . . . . . . . . . 329 Inhaltsverzeichnis XV 15.3.2 Map-Filter-Reduce auf Maps . . . . . . . . . . . . . . . . . . . . . . . . 329 15.3.3 Prädikate höherer Ordnung auf Maps . . . . . . . . . . . . . . . . 331 15.4 Maps und Funktionen: Zwei Seiten einer Medaille . . . . . . . . . . . . 333 15.5 Maps, Funktionen und Memoization . . . . . . . . . . . . . . . . . . . . . . . 334 16 Beispiel: Synthese von Programmen . . . . . . . . . . . . . . . . . . . . . . . 337 16.1 Globale Suche . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 338 16.1.1 Suchräume . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 338 16.1.2 Einschränkung von Suchräumen . . . . . . . . . . . . . . . . . . . . . 339 16.1.3 Suchräume und partielle Lösungen . . . . . . . . . . . . . . . . . . . 340 16.1.4 Basisregeln für Suchräume . . . . . . . . . . . . . . . . . . . . . . . . . . 340 16.2 Problemlösungen als Maps . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 341 16.2.1 Beispiel: Das n-Damen-Problem . . . . . . . . . . . . . . . . . . . . . 341 16.2.2 Suchräume als Mengen von Maps . . . . . . . . . . . . . . . . . . . . 343 16.2.3 Constraints auf Mengen von Maps . . . . . . . . . . . . . . . . . . . 344 16.3 Programmableitung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 345 16.3.1 Das n-Damen-Problem – Von der Spezifikation zum Algorithmus . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 346 16.3.2 Ein allgemeines Schema für die globale Suche . . . . . . . . . 350 16.4 Beispiel: Scheduling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 353 Teil V Integration von Paradigmen 17 Zeit und Zustand in der funktionalen Welt . . . . . . . . . . . . . . . . 359 17.1 Zeit und Zustand: Zwei Seiten einer Medaille . . . . . . . . . . . . . . . . 360 17.1.1 Ein kleines Beispiel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 362 17.2 Monaden: Ein schicker Name für Altbekanntes . . . . . . . . . . . . . . 364 17.2.1 Programmieren mit Continuations . . . . . . . . . . . . . . . . . . . 366 17.2.2 Continuations + Hiding = Monaden . . . . . . . . . . . . . . . . . 368 17.2.3 Die Ein-/Ausgabe ist eine Compiler-interne Monade . . . 369 17.3 Zeit: Die elementarste aller Zustands-Monaden . . . . . . . . . . . . . . 369 17.3.1 Zeitabhängige Operationen und Evolution . . . . . . . . . . . . 371 17.3.2 Zeit-Monade oder Zustands-Monade? . . . . . . . . . . . . . . . . 374 17.4 Die erweiterte Zeit-Monade . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 375 17.4.1 Exceptions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 375 17.4.2 Choice . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 377 17.4.3 Die Systemuhr und Timeouts . . . . . . . . . . . . . . . . . . . . . . . 378 17.4.4 Zusammenfassung: Die Zeit-Monade . . . . . . . . . . . . . . . . . 378 18 Objekte und Ein-/Ausgabe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 381 18.1 Objekte als zeitabhängige Werte . . . . . . . . . . . . . . . . . . . . . . . . . . . 381 18.1.1 Spezielle Notationen für Objekte und Klassen . . . . . . . . . 385 18.1.2 „Globale“ Objekte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 387 18.2 Laufzeitsystem und andere Objekte (Zeit-Monaden) . . . . . . . . . 389 XVI Inhaltsverzeichnis 18.2.1 Dateioperationen höherer Ordnung . . . . . . . . . . . . . . . . . . 392 18.2.2 Ein typisiertes Dateisystem? . . . . . . . . . . . . . . . . . . . . . . . . 393 19 Agenten und Prozesse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 395 19.1 Service-orientierte Architekturen . . . . . . . . . . . . . . . . . . . . . . . . . . 396 19.2 Agenten als Monaden . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 398 19.3 Kommunikation: Service-Access-Points . . . . . . . . . . . . . . . . . . . . . 400 19.4 Ein Beispiel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 405 19.5 „Globale“ Agenten und SAPs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 411 19.6 Spezialfälle: Kanäle und Gates . . . . . . . . . . . . . . . . . . . . . . . . . . . . 412 19.6.1 Kanäle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 412 19.6.2 Gates: SAPs + Agenten . . . . . . . . . . . . . . . . . . . . . . . . . . . . 414 19.7 OPAL, CONCURRENT HASKELL, EDEN und ERLANG . . . . . . 418 20 Graphische Schnittstellen (GUIs) . . . . . . . . . . . . . . . . . . . . . . . . . . 421 20.1 GUIs – ein Konzept mit drei Dimensionen . . . . . . . . . . . . . . . . . . 422 20.2 Die Applikation (Model) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 423 20.3 Graphische Gestaltung (View) . . . . . . . . . . . . . . . . . . . . . . . . . . . . 425 20.3.1 Arten von GUI-Elementen . . . . . . . . . . . . . . . . . . . . . . . . . . 427 20.3.2 Stil-Information . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 430 20.3.3 Geometrische Anordnung . . . . . . . . . . . . . . . . . . . . . . . . . . . 430 20.3.4 The Big Picture . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 433 20.4 Interaktion mit der Applikation (Control) . . . . . . . . . . . . . . . . . . 434 20.4.1 Das Fenster als Agent . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 434 20.4.2 Emitter als Kanäle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 435 20.4.3 Regulator-Gates . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 437 20.4.4 Weitere Gates . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 440 20.4.5 Ereignisse – Events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 440 20.5 HAGGIS, FUDGETS, FRANTK und andere . . . . . . . . . . . . . . . . . . 441 21 Massiv parallele Programme . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 443 21.1 Skeletons: Parallelität durch spezielle Funktionale . . . . . . . . . . . 444 21.2 Cover: Aufteilung des Datenraums . . . . . . . . . . . . . . . . . . . . . . . . . 446 21.2.1 Spezifikation von Covern . . . . . . . . . . . . . . . . . . . . . . . . . . . 447 21.2.2 Skelette über Covern . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 449 21.2.3 Matrix-Cover . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 451 21.3 Beispiel: Matrixmultiplikation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 452 21.4 Von Skeletons zum Message passing . . . . . . . . . . . . . . . . . . . . . . . 456 22 Integration von Konzepten anderer Programmierparadigmen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 459 22.1 Programmierparadigmen und deren Integration . . . . . . . . . . . . . 459 22.2 Objektorientierte Erweiterungen funktionaler Sprachen . . . . . . . 461 22.2.1 HASKELL++ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 462 22.2.2 O’HASKELL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 462 Inhaltsverzeichnis XVII 22.2.3 OCAML . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 464 22.3 Funktional-logische Programmierung und darüber hinaus . . . . . 464 22.4 Fazit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 467 Literatur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 469 Index . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 479 Hinweis: Eine Errata-Liste und weitere Hinweise zu diesem Buch sind über die Web-Adresse http://www.uebb.cs.tu-berlin.de/books/fp zu erreichen. Teil I Elementare Funktionale Programmierung Eine Wiederholung Wer Fortgeschrittenes diskutieren will, muss sich erst einmal über das Elementare im Klaren sein. Das gilt für Programmierkonzepte genauso wie für alle anderen Betätigungen. Es gibt eine Fülle von einführenden Büchern zum Thema „Funktionale Programmierung“, unter anderem [20, 22, 32, 47, 50, 81, 102, 111, 122]. Diese Auswahl von Büchern aus den letzten fünfzehn Jahren zeigt, dass das Thema eine stetige Weiterentwicklung erfahren hat, aber auch, dass es Sackgassen gegeben hat. Wir werden uns primär mit neueren Konzepten befassen, teilweise mit bereits anerkannten und etablierten, teilweise aber auch mit hochgradig experimentellen. Dabei gehen wir davon aus, dass dem Leser die elementaren Grundkonzepte der Funktionalen Programmierung, wie sie in den obigen Büchern beschrieben werden, bereits bekannt sind. Um einen wohldefinierten Aufsetzpunkt zu haben, beginnen wir mit einem kurzen Rückblick auf die elementaren Grundlagen der Funktionalen Programmierung und vor allem mit einigen Vereinbarungen zur Notation, die wir im Folgenden verwenden. In unserer Darstellung orientieren wir uns primär an [111], aber auch an [81] oder [20] – ohne allerdings die dortigen Schreibweisen komplett zu übernehmen. 0 Das Strittigste vorab: Notationen Über Geschmack lässt sich nicht streiten. (Sprichwort) Natürlich lässt sich – entgegen anderslautenden Sprichwörtern – über Geschmack ganz trefflich streiten. Einer Anekdote zufolge sollen sich einst international hochreputierte Professoren wütend über die Frage gezankt haben, ob man eine Integervariable mittels int x oder x : int einführen sollte. Die erste Variante orientiert sich an der umgangssprachlichen Formulierung „die Integervariable x“ und hat von algol aus ihren Weg z. B. in c, c++ und von da aus weiter in java gefunden. Die zweite Variante orientiert sich an der mathematischen Notation x ∈ Int und drang von pascal aus z. B. in modula und ada vor. Die Erkenntnis, dass ein solcher Streit wohl müßig ist, hat zu einer Gegenbewegung geführt, die Notation für völlig belanglos erklärte und ausschließlich über Konzepte sprach. Doch beweist die Zahl der gescheiterten Sprachen, die exzellente Konzepte mit völlig unlesbarer Syntax verbanden, dass Notation eben doch nicht ganz vernachlässigbar ist. In der Quintessenz hat sich die Erkenntnis durchgesetzt, dass eine Sprache zwar primär an ihren Konzepten zu messen ist, dass sie diese aber in brauchbaren Notationen vermitteln muss. Ein Buch, das sich modernes Sprachdesign zum Thema gesetzt hat, darf deshalb das Thema Notationen nicht ignorieren. Und modern heißt heute auch, dass man die Standards übernehmen muss, die von graphischen Bildschirm- und Drucksystemen sowie von Setzwerkzeugen wie TEX und Postscript vorgegeben wurden. Das führt zu einer Sicht auf Notationen, die von gängigen Mustern bei Programmiersprachen radikal abweicht. 4 0 Das Strittigste vorab: Notationen 0.1 Jenseits von ASCII Die Designer von Programmiersprachen scheinen eine merkwürdige Affinität zu den guten alten ascii-Terminals zu haben. Anders ist es kaum zu erklären, dass die größte notationelle Revolution der letzten dreißig Jahre das Zulassen von unicode-Zeichen in java war. Dabei gibt es spätestens seit den Zeiten von TEX und mathematica keinen Grund mehr, an dieser Beschränkung festzuhalten. Und wer jemals einen Kollegen in China beobachtet hat, wie er seitenweise chinesischen Text aus einem ascii-Terminal zaubert, muss zugeben, dass die Konformität zwischen Input und lesbarem Output auch nur ein vorgeschobenes Argument ist. Mathematische Notation Vor diesem Hintergrund machen sich einige Informatiker wie z. B. Guy Steele von der Firma SUN inzwischen dafür stark, die Schreibweisen von Programmiersprachen radikal zu modernisieren. Es gibt schließlich keinen Grund, weshalb Programme so viel altmodischer aussehen müssen als Mathematik. Tabelle 0.1 zeigt, dass derselbe Programmtext in ganz verschiedenen Notationen dargestellt werden kann. ascii unicode TEX rho0 = r DOT r ρ0 = r · r ρ0 = r · r v v norm = v / ||v|| P [k=1:n] a[k] x k v vnorm = v Pn k k=1 ak x norm = v / norm v SUM[k=1:n] a[k] x k Tab. 0.1: Verschiedene Schreibweisen eines Programmfragments (nach G. Steele) Wir werden uns in diesem Buch weitestgehend an die dritte – also die fortschrittlichste – dieser Varianten halten. Fonts Neben mathematischen Zeichen bieten moderne Textsysteme auch die Möglichkeit, mit unterschiedlichen Fonts zu arbeiten. Allerdings besteht dabei die Gefahr, zu viel des Guten zu tun und ein extrem unruhiges Satzbild zu erzeugen. Wir suchen hier einen Kompromiss und verwenden die Fonts und Konventionen aus Tabelle 0.2. Namen von Werten, Funktionen etc. schreiben wir (meist) klein und – mathematischer Tradition folgend – kursiv. Wie auch bei allen anderen Symbolen erlauben wir Indizierung ebenso wie Annotation mit Strichen. Typen und Strukturen werden ebenfalls kursiv geschrieben, allerdings beginnend mit einem Großbuchstaben. Für Typklassen verwenden wir einen speziellen Font.