Die hundertjährige Sprache
April 2003
(Dieser Aufsatz basiert auf einer Keynote-Präsentation auf der PyCon 2003.)
Es ist schwer vorherzusagen, wie das Leben in hundert Jahren sein wird. Nur wenige Dinge können wir mit Sicherheit sagen. Wir wissen, dass jeder fliegende Autos fahren wird, dass Bebauungspläne gelockert werden, um Gebäude mit Hunderten von Stockwerken zu erlauben, dass es die meiste Zeit dunkel sein wird und dass Frauen alle in Kampfkünsten ausgebildet sein werden. Hier möchte ich auf ein Detail dieses Bildes zoomen. Welche Art von Programmiersprache werden sie verwenden, um die Software zu schreiben, die diese fliegenden Autos steuert?
Das ist es wert, darüber nachzudenken, nicht so sehr, weil wir diese Sprachen tatsächlich benutzen werden, sondern weil wir, wenn wir Glück haben, Sprachen auf dem Weg von diesem Punkt dorthin benutzen werden.
Ich denke, dass Sprachen, wie Arten, evolutionäre Bäume bilden werden, mit Sackgassen, die sich überall abzweigen. Wir sehen das bereits geschehen. Cobol scheint trotz seiner früheren Popularität keine intellektuellen Nachkommen zu haben. Es ist eine evolutionäre Sackgasse – eine neandertalerische Sprache.
Ich prophezeie Java ein ähnliches Schicksal. Leute schicken mir manchmal E-Mails und sagen: „Wie kannst du sagen, dass Java keine erfolgreiche Sprache sein wird? Sie ist bereits eine erfolgreiche Sprache.“ Und ich gebe zu, dass sie das ist, wenn man Erfolg an der Regalfläche misst, die von Büchern darüber (insbesondere einzelnen Büchern darüber) eingenommen wird, oder an der Anzahl von Studenten, die glauben, sie lernen zu müssen, um einen Job zu bekommen. Wenn ich sage, dass Java keine erfolgreiche Sprache sein wird, meine ich etwas Spezifischeres: dass Java eine evolutionäre Sackgasse sein wird, wie Cobol.
Das ist nur eine Vermutung. Ich könnte falsch liegen. Mein Punkt hier ist nicht, Java schlechtzumachen, sondern die Frage der evolutionären Bäume aufzuwerfen und die Leute dazu zu bringen, sich zu fragen: Wo auf dem Baum ist Sprache X? Der Grund, diese Frage zu stellen, ist nicht nur, damit unsere Geister in hundert Jahren sagen können: Ich habe es euch ja gesagt. Es ist, weil die Nähe zu den Hauptästen eine nützliche Heuristik ist, um Sprachen zu finden, in denen es sich jetzt gut programmieren lässt.
Zu jedem Zeitpunkt ist man wahrscheinlich auf den Hauptästen eines evolutionären Baumes am glücklichsten. Selbst als es noch genügend Neandertaler gab, muss es schlimm gewesen sein, einer zu sein. Die Cro-Magnons hätten einen ständig verprügelt und einem das Essen gestohlen.
Der Grund, warum ich wissen möchte, wie Sprachen in hundert Jahren sein werden, ist, damit ich weiß, auf welchen Ast des Baumes ich jetzt setzen soll.
Die Evolution von Sprachen unterscheidet sich von der Evolution von Arten, weil Äste konvergieren können. Der Fortran-Ast scheint sich beispielsweise mit den Nachkommen von Algol zu vereinigen. Theoretisch ist dies auch für Arten möglich, aber es ist unwahrscheinlich, dass es bei etwas Größerem als einer Zelle passiert ist.
Konvergenz ist bei Sprachen wahrscheinlicher, teilweise weil der Möglichkeitsraum kleiner ist und teilweise, weil Mutationen nicht zufällig sind. Sprachdesigner übernehmen bewusst Ideen aus anderen Sprachen.
Es ist besonders nützlich für Sprachdesigner, darüber nachzudenken, wohin die Evolution von Programmiersprachen wahrscheinlich führen wird, weil sie entsprechend steuern können. In diesem Fall wird „auf einem Hauptast bleiben“ mehr als nur eine Möglichkeit, eine gute Sprache zu wählen. Es wird zu einer Heuristik, um die richtigen Entscheidungen über das Sprachdesign zu treffen.
Jede Programmiersprache kann in zwei Teile unterteilt werden: eine Reihe von grundlegenden Operatoren, die die Rolle von Axiomen spielen, und den Rest der Sprache, der prinzipiell in Bezug auf diese grundlegenden Operatoren geschrieben werden könnte.
Ich denke, die grundlegenden Operatoren sind der wichtigste Faktor für das langfristige Überleben einer Sprache. Den Rest kann man ändern. Es ist wie die Regel, dass man beim Kauf eines Hauses zuerst auf den Standort achten sollte. Alles andere kann man später reparieren, aber den Standort kann man nicht reparieren.
Ich denke, es ist wichtig, dass die Axiome nicht nur gut gewählt sind, sondern dass es nur wenige davon gibt. Mathematiker haben das schon immer so gesehen – je weniger, desto besser – und ich denke, sie haben Recht.
Zumindest muss es eine nützliche Übung sein, sich den Kern einer Sprache genau anzusehen, um zu sehen, ob es Axiome gibt, die ausgemerzt werden könnten. Ich habe in meiner langen Karriere als Schlendrian gelernt, dass Krümel Krümel hervorbringen, und das habe ich in Software ebenso gesehen wie unter Betten und in Zimmerecken.
Ich habe die Ahnung, dass die Hauptäste des evolutionären Baumes durch die Sprachen mit den kleinsten, saubersten Kernen verlaufen. Je mehr von einer Sprache man in sich selbst schreiben kann, desto besser.
Natürlich mache ich eine große Annahme, indem ich überhaupt frage, wie Programmiersprachen in hundert Jahren sein werden. Werden wir in hundert Jahren überhaupt noch Programme schreiben? Werden wir Computern nicht einfach sagen, was wir von ihnen wollen?
In diesem Bereich gab es bisher nicht viel Fortschritt. Meine Vermutung ist, dass Menschen in hundert Jahren Computern immer noch sagen werden, was sie tun sollen, indem sie Programme verwenden, die wir als solche erkennen würden. Es mag Aufgaben geben, die wir heute durch das Schreiben von Programmen lösen und für die man in hundert Jahren keine Programme mehr schreiben muss, aber ich denke, es wird immer noch eine gute Menge an Programmierung der Art geben, wie wir sie heute betreiben.
Es mag vermessen erscheinen, zu glauben, dass jemand vorhersagen kann, wie irgendeine Technologie in hundert Jahren aussehen wird. Aber denken Sie daran, dass wir bereits fast fünfzig Jahre Geschichte hinter uns haben. Hundert Jahre vorauszuschauen ist eine greifbare Vorstellung, wenn wir bedenken, wie langsam sich Sprachen in den letzten fünfzig Jahren entwickelt haben.
Sprachen entwickeln sich langsam, weil sie keine wirklichen Technologien sind. Sprachen sind Notation. Ein Programm ist eine formale Beschreibung des Problems, das Sie von einem Computer lösen lassen möchten. Daher ähnelt die Entwicklungsrate von Programmiersprachen eher der Entwicklungsrate mathematischer Notation als beispielsweise Transport oder Kommunikation. Mathematische Notation entwickelt sich zwar, aber nicht mit den riesigen Sprüngen, die man in der Technologie sieht.
Woraus Computer in hundert Jahren auch immer bestehen mögen, es scheint sicher zu sein, vorherzusagen, dass sie viel schneller sein werden als jetzt. Wenn das Mooresche Gesetz weiterhin gilt, werden sie 74 Trillionen (73.786.976.294.838.206.464) Mal schneller sein. Das ist schwer vorstellbar. Und tatsächlich könnte die wahrscheinlichste Vorhersage in Bezug auf die Geschwindigkeit sein, dass das Mooresche Gesetz nicht mehr funktioniert. Alles, was sich alle achtzehn Monate verdoppeln soll, wird wahrscheinlich irgendwann auf eine grundlegende Grenze stoßen. Aber ich glaube ohne weiteres, dass Computer sehr viel schneller sein werden. Selbst wenn sie nur eine magere Million Mal schneller sind, sollte das die Spielregeln für Programmiersprachen erheblich ändern. Unter anderem wird es mehr Raum für das geben, was heute als langsame Sprachen betrachtet würde, also Sprachen, die keinen sehr effizienten Code liefern.
Und doch werden einige Anwendungen immer noch Geschwindigkeit erfordern. Einige der Probleme, die wir mit Computern lösen wollen, werden von Computern selbst erzeugt; zum Beispiel hängt die Rate, mit der Sie Videobilder verarbeiten müssen, von der Rate ab, mit der ein anderer Computer sie erzeugen kann. Und es gibt eine weitere Klasse von Problemen, die von Natur aus eine unbegrenzte Kapazität haben, Zyklen zu absorbieren: Bildrendering, Kryptographie, Simulationen.
Wenn einige Anwendungen zunehmend ineffizient sein können, während andere weiterhin die gesamte Geschwindigkeit benötigen, die die Hardware liefern kann, werden schnellere Computer bedeuten, dass Sprachen eine immer größere Bandbreite an Effizienzen abdecken müssen. Das sehen wir bereits. Aktuelle Implementierungen einiger beliebter neuer Sprachen sind nach den Maßstäben früherer Jahrzehnte schockierend verschwenderisch.
Das ist nicht nur etwas, das bei Programmiersprachen passiert. Es ist ein allgemeiner historischer Trend. Wenn Technologien sich verbessern, kann jede Generation Dinge tun, die die vorherige Generation als verschwenderisch betrachtet hätte. Menschen vor dreißig Jahren wären erstaunt gewesen, wie beiläufig wir Ferngespräche führen. Menschen vor hundert Jahren wären noch erstaunter gewesen, dass ein Paket eines Tages über Memphis von Boston nach New York reisen würde.
Ich kann Ihnen jetzt schon sagen, was mit all den zusätzlichen Zyklen passieren wird, die uns die schnellere Hardware in den nächsten hundert Jahren bringen wird. Sie werden fast alle verschwendet.
Ich habe gelernt zu programmieren, als Computerleistung knapp war. Ich erinnere mich, wie ich alle Leerzeichen aus meinen Basic-Programmen entfernt habe, damit sie in den Speicher eines 4K TRS-80 passten. Der Gedanke an all diese stupend ineffiziente Software, die Zyklen verbrennt, indem sie immer wieder dasselbe tut, erscheint mir irgendwie widerlich. Aber ich denke, meine Intuition ist hier falsch. Ich bin wie jemand, der arm aufgewachsen ist und kein Geld ausgeben kann, selbst für etwas Wichtiges wie einen Arztbesuch.
Manche Arten von Verschwendung sind wirklich ekelhaft. SUVs zum Beispiel wären wohl ekelhaft, selbst wenn sie mit einem Treibstoff laufen würden, der niemals zur Neige geht und keine Umweltverschmutzung verursacht. SUVs sind ekelhaft, weil sie die Lösung für ein ekelhaftes Problem sind. (Wie man Minivans maskuliner aussehen lässt.) Aber nicht jede Verschwendung ist schlecht. Jetzt, wo wir die Infrastruktur dafür haben, scheint das Zählen der Minuten Ihrer Ferngespräche kleinlich. Wenn Sie die Ressourcen haben, ist es eleganter, alle Anrufe als eine Art von Ding zu betrachten, egal wo sich die andere Person befindet.
Es gibt gute Verschwendung und schlechte Verschwendung. Ich interessiere mich für gute Verschwendung – die Art, bei der wir durch mehr Ausgaben einfachere Designs erzielen können. Wie werden wir die Möglichkeiten zur Verschwendung von Zyklen nutzen, die uns neue, schnellere Hardware bieten wird?
Der Wunsch nach Geschwindigkeit ist so tief in uns verwurzelt, mit unseren winzigen Computern, dass es bewusste Anstrengung erfordern wird, ihn zu überwinden. Im Sprachdesign sollten wir bewusst nach Situationen suchen, in denen wir Effizienz gegen auch nur die geringste Verbesserung der Bequemlichkeit eintauschen können.
Die meisten Datenstrukturen existieren wegen der Geschwindigkeit. Zum Beispiel haben viele Sprachen heute sowohl Strings als auch Listen. Semantisch sind Strings mehr oder weniger eine Teilmenge von Listen, bei denen die Elemente Zeichen sind. Warum braucht man also einen separaten Datentyp? Eigentlich nicht. Strings existieren nur aus Effizienzgründen. Aber es ist lahm, die Semantik der Sprache mit Hacks zu überladen, um Programme schneller laufen zu lassen. Das Vorhandensein von Strings in einer Sprache scheint ein Fall von vorzeitiger Optimierung zu sein.
Wenn wir den Kern einer Sprache als eine Menge von Axiomen betrachten, ist es sicherlich ekelhaft, zusätzliche Axiome zu haben, die keine Ausdruckskraft hinzufügen, nur um der Effizienz willen. Effizienz ist wichtig, aber ich glaube nicht, dass das der richtige Weg ist, sie zu erreichen.
Der richtige Weg, dieses Problem zu lösen, denke ich, ist die Trennung der Bedeutung eines Programms von den Implementierungsdetails. Anstatt sowohl Listen als auch Strings zu haben, sollte man nur Listen haben, mit einer Möglichkeit, dem Compiler Optimierungshinweise zu geben, die es ihm ermöglichen, Strings bei Bedarf als zusammenhängende Bytes zu legen.
Da Geschwindigkeit in den meisten Teilen eines Programms keine Rolle spielt, müssen Sie sich normalerweise nicht mit dieser Art von Mikromanagement befassen. Das wird mit zunehmender Geschwindigkeit der Computer immer mehr der Fall sein.
Weniger über die Implementierung zu sagen, sollte Programme auch flexibler machen. Spezifikationen ändern sich, während ein Programm geschrieben wird, und das ist nicht nur unvermeidlich, sondern auch wünschenswert.
Das Wort „Essay“ kommt vom französischen Verb „essayer“, was „versuchen“ bedeutet. Ein Essay ist im ursprünglichen Sinne etwas, das man schreibt, um etwas herauszufinden. Das passiert auch in der Software. Ich denke, einige der besten Programme waren Essays, in dem Sinne, dass die Autoren beim Start nicht genau wussten, was sie schreiben wollten.
Lisp-Hacker wissen bereits um den Wert der Flexibilität bei Datenstrukturen. Wir neigen dazu, die erste Version eines Programms so zu schreiben, dass es alles mit Listen erledigt. Diese ersten Versionen können so schockierend ineffizient sein, dass es bewusste Anstrengung erfordert, nicht darüber nachzudenken, was sie tun, so wie es für mich zumindest bewusste Anstrengung erfordert, ein Steak zu essen und nicht darüber nachzudenken, woher es kommt.
Was Programmierer in hundert Jahren am meisten suchen werden, ist eine Sprache, in der man mit dem geringsten Aufwand eine unglaublich ineffiziente Version 1 eines Programms zusammenstellen kann. Zumindest würden wir es in heutigen Begriffen so beschreiben. Sie werden sagen, dass sie eine Sprache wollen, die einfach zu programmieren ist.
Ineffiziente Software ist nicht ekelhaft. Ekelhaft ist eine Sprache, die Programmierer zu unnötiger Arbeit zwingt. Zeitverschwendung für Programmierer ist die wahre Ineffizienz, nicht Zeitverschwendung für Maschinen. Das wird mit zunehmender Geschwindigkeit der Computer immer deutlicher.
Ich denke, die Abschaffung von Strings ist bereits etwas, über das wir nachdenken könnten. Wir haben es in Arc getan, und es scheint ein Gewinn zu sein; einige Operationen, die als reguläre Ausdrücke umständlich zu beschreiben wären, können leicht als rekursive Funktionen beschrieben werden.
Wie weit wird diese Abflachung von Datenstrukturen gehen? Ich kann mir Möglichkeiten vorstellen, die selbst mich mit meinem gewissenhaft erweiterten Geist schockieren. Werden wir zum Beispiel Arrays abschaffen? Schließlich sind sie nur eine Teilmenge von Hash-Tabellen, bei denen die Schlüssel Vektoren von ganzen Zahlen sind. Werden wir Hash-Tabellen selbst durch Listen ersetzen?
Es gibt noch schockierendere Aussichten. Das Lisp, das McCarthy 1960 beschrieb, hatte zum Beispiel keine Zahlen. Logisch gesehen muss man keine separate Vorstellung von Zahlen haben, da man sie als Listen darstellen kann: Die ganze Zahl n könnte als eine Liste von n Elementen dargestellt werden. Man kann damit rechnen. Es ist nur unerträglich ineffizient.
Niemand hat ernsthaft vorgeschlagen, Zahlen praktisch als Listen zu implementieren. Tatsächlich war McCarthys Arbeit von 1960 zu der Zeit nicht zur Implementierung gedacht. Es war eine theoretische Übung, ein Versuch, eine elegantere Alternative zur Turing-Maschine zu schaffen. Als jemand diese Arbeit unerwartet nahm und sie in einen funktionierenden Lisp-Interpreter übersetzte, wurden Zahlen sicherlich nicht als Listen dargestellt; sie wurden binär dargestellt, wie in jeder anderen Sprache auch.
Könnte eine Programmiersprache so weit gehen, Zahlen als grundlegenden Datentyp abzuschaffen? Ich frage das nicht so sehr als ernsthafte Frage, sondern als eine Möglichkeit, mit der Zukunft zu spielen. Es ist wie der hypothetische Fall einer unwiderstehlichen Kraft, die auf ein unbewegliches Objekt trifft – hier eine unvorstellbar ineffiziente Implementierung, die auf unvorstellbar große Ressourcen trifft. Ich sehe keinen Grund, warum nicht. Die Zukunft ist ziemlich lang. Wenn es etwas gibt, das wir tun können, um die Anzahl der Axiome im Kern der Sprache zu reduzieren, dann wäre das die Seite, auf die man setzen sollte, wenn t gegen unendlich geht. Wenn die Idee in hundert Jahren immer noch unerträglich erscheint, vielleicht dann nicht in tausend.
Nur um das klarzustellen: Ich schlage nicht vor, dass alle numerischen Berechnungen tatsächlich mit Listen durchgeführt würden. Ich schlage vor, dass die Kernsprache, vor allen zusätzlichen Notationen zur Implementierung, so definiert wird. In der Praxis würde jedes Programm, das viel rechnen möchte, Zahlen wahrscheinlich binär darstellen, aber das wäre eine Optimierung, kein Teil der Kernsprachensemantik.
Eine weitere Möglichkeit, Zyklen zu verbrennen, ist die Verwendung vieler Software-Schichten zwischen der Anwendung und der Hardware. Auch das ist ein Trend, den wir bereits sehen: Viele neuere Sprachen werden in Bytecode kompiliert. Bill Woods sagte mir einmal, dass jede Interpretationsebene als Faustregel einen Geschwindigkeitsverlust von Faktor 10 kostet. Diese zusätzlichen Kosten kaufen Ihnen Flexibilität.
Die allererste Version von Arc war ein extremes Beispiel für diese Art von mehrstufiger Langsamkeit mit entsprechenden Vorteilen. Es war ein klassischer „metacircularer“ Interpreter, der auf Common Lisp aufbaute, mit einer deutlichen Familienähnlichkeit zur eval-Funktion, die in McCarthys ursprünglicher Lisp-Arbeit definiert wurde. Das Ganze war nur ein paar hundert Codezeilen lang, daher war es sehr einfach zu verstehen und zu ändern. Das Common Lisp, das wir verwendeten, CLisp, läuft selbst auf einem Bytecode-Interpreter. Wir hatten also zwei Interpretationsebenen, eine davon (die obere) schockierend ineffizient, und die Sprache war nutzbar. Kaum nutzbar, gebe ich zu, aber nutzbar.
Das Schreiben von Software als mehrere Schichten ist selbst innerhalb von Anwendungen eine leistungsstarke Technik. Bottom-up-Programmierung bedeutet, ein Programm als eine Reihe von Schichten zu schreiben, von denen jede als Sprache für die darüber liegende dient. Dieser Ansatz führt tendenziell zu kleineren, flexibleren Programmen. Es ist auch der beste Weg zu diesem Heiligen Gral, der Wiederverwendbarkeit. Eine Sprache ist per Definition wiederverwendbar. Je mehr Ihrer Anwendung Sie in eine Sprache für die Erstellung dieser Art von Anwendung verschieben können, desto mehr Ihrer Software wird wiederverwendbar sein.
Irgendwie wurde die Idee der Wiederverwendbarkeit in den 1980er Jahren mit der objektorientierten Programmierung verbunden, und keine Menge an gegenteiligen Beweisen scheint sie davon abbringen zu können. Aber obwohl einige objektorientierte Software wiederverwendbar ist, ist das, was sie wiederverwendbar macht, ihre Bottom-up-Natur, nicht ihre Objektorientiertheit. Betrachten Sie Bibliotheken: Sie sind wiederverwendbar, weil sie Sprachen sind, egal ob sie in einem objektorientierten Stil geschrieben sind oder nicht.
Ich sage übrigens nicht den Untergang der objektorientierten Programmierung voraus. Obwohl ich nicht glaube, dass sie guten Programmierern viel zu bieten hat, außer in bestimmten spezialisierten Domänen, ist sie für große Organisationen unwiderstehlich. Objektorientierte Programmierung bietet eine nachhaltige Möglichkeit, Spaghetti-Code zu schreiben. Sie ermöglicht es Ihnen, Programme als eine Reihe von Patches anzuhäufen. Große Organisationen entwickeln Software immer auf diese Weise, und ich erwarte, dass dies in hundert Jahren genauso zutrifft wie heute.
Solange wir über die Zukunft sprechen, sollten wir auch über parallele Berechnungen sprechen, denn dort scheint diese Idee zu leben. Das heißt, egal wann Sie sprechen, parallele Berechnungen scheinen etwas zu sein, das in der Zukunft passieren wird.
Wird die Zukunft sie jemals einholen? Die Leute sprechen seit mindestens 20 Jahren über parallele Berechnungen als etwas Unmittelbares, und es hat die Programmierpraxis bisher nicht wesentlich beeinflusst. Oder hat es das nicht? Bereits Chip-Designer müssen darüber nachdenken, und das müssen auch Leute, die versuchen, Systemsoftware auf Multi-CPU-Computern zu schreiben.
Die eigentliche Frage ist, wie weit nach oben in der Abstraktionsleiter wird Parallelität gehen? Wird sie in hundert Jahren sogar Anwendungsentwickler betreffen? Oder wird es etwas sein, über das Compiler-Entwickler nachdenken, das aber im Quellcode von Anwendungen normalerweise unsichtbar ist?
Eines, das wahrscheinlich ist, ist, dass die meisten Möglichkeiten für Parallelität verschwendet werden. Dies ist ein Sonderfall meiner allgemeineren Vorhersage, dass die meiste zusätzliche Computerleistung, die uns gegeben wird, verschwendet wird. Ich erwarte, dass, wie bei der stupenden Geschwindigkeit der zugrunde liegenden Hardware, Parallelität etwas sein wird, das verfügbar ist, wenn man es explizit anfordert, aber normalerweise nicht genutzt wird. Dies impliziert, dass die Art der Parallelität, die wir in hundert Jahren haben werden, außer in speziellen Anwendungen, keine massive Parallelität sein wird. Ich erwarte, dass sie für normale Programmierer eher darin besteht, Prozesse abzuspalten, die alle parallel laufen.
Und dies wird, wie die Anforderung spezifischer Implementierungen von Datenstrukturen, etwas sein, das man ziemlich spät im Lebenszyklus eines Programms tut, wenn man versucht, es zu optimieren. Version 1 wird gewöhnlich alle Vorteile der parallelen Berechnung ignorieren, genauso wie sie Vorteile spezifischer Datenrepräsentationen ignorieren wird.
Außer in speziellen Arten von Anwendungen wird Parallelität nicht die Programme durchdringen, die in hundert Jahren geschrieben werden. Es wäre vorzeitige Optimierung, wenn sie es täte.
Wie viele Programmiersprachen wird es in hundert Jahren geben? In letzter Zeit scheint es eine riesige Anzahl neuer Programmiersprachen zu geben. Ein Grund dafür ist, dass schnellere Hardware es Programmierern ermöglicht hat, unterschiedliche Kompromisse zwischen Geschwindigkeit und Bequemlichkeit einzugehen, abhängig von der Anwendung. Wenn dies ein echter Trend ist, sollte die Hardware, die wir in hundert Jahren haben werden, ihn nur noch verstärken.
Und doch könnte es in hundert Jahren nur wenige weit verbreitete Sprachen geben. Ein Grund dafür ist Optimismus: Es scheint, dass man, wenn man wirklich gute Arbeit geleistet hat, eine Sprache schaffen könnte, die ideal für das Schreiben einer langsamen Version 1 ist, und die dennoch mit den richtigen Optimierungshinweisen an den Compiler sehr schnellen Code liefert, wenn nötig. Da ich optimistisch bin, werde ich daher vorhersagen, dass Programmierer in hundert Jahren trotz der riesigen Lücke zwischen akzeptabler und maximaler Effizienz Sprachen haben werden, die den größten Teil davon abdecken können.
Mit zunehmender Lücke werden Profiler immer wichtiger. Derzeit wird dem Profiling wenig Aufmerksamkeit geschenkt. Viele Leute scheinen immer noch zu glauben, dass der Weg zu schnellen Anwendungen darin besteht, Compiler zu schreiben, die schnellen Code generieren. Da die Lücke zwischen akzeptabler und maximaler Leistung wächst, wird immer deutlicher, dass der Weg zu schnellen Anwendungen darin besteht, eine gute Anleitung von der einen zur anderen zu haben.
Wenn ich sage, dass es vielleicht nur wenige Sprachen geben wird, meine ich keine domänenspezifischen „kleinen Sprachen“. Ich halte solche eingebetteten Sprachen für eine großartige Idee und erwarte, dass sie sich verbreiten werden. Aber ich erwarte, dass sie als dünne genug Häute geschrieben werden, damit Benutzer die allgemeine Sprache darunter sehen können.
Wer wird die Sprachen der Zukunft entwerfen? Einer der aufregendsten Trends der letzten zehn Jahre war der Aufstieg von Open-Source-Sprachen wie Perl, Python und Ruby. Das Sprachdesign wird von Hackern übernommen. Die bisherigen Ergebnisse sind unordentlich, aber ermutigend. Es gibt zum Beispiel einige verblüffend neuartige Ideen in Perl. Viele sind verblüffend schlecht, aber das gilt immer für ehrgeizige Bemühungen. Mit seiner aktuellen Mutationsrate, Gott weiß, wozu Perl in hundert Jahren mutieren könnte.
Es stimmt nicht, dass die, die nicht können, lehren (einige der besten Hacker, die ich kenne, sind Professoren), aber es stimmt, dass es viele Dinge gibt, die die, die lehren, nicht können. Forschung auferlegt einschränkende Kastenbeschränkungen. In jedem akademischen Fach gibt es Themen, die man bearbeiten darf und andere, die man nicht bearbeiten darf. Leider basiert die Unterscheidung zwischen akzeptablen und verbotenen Themen normalerweise darauf, wie intellektuell die Arbeit klingt, wenn sie in Forschungsarbeiten beschrieben wird, anstatt darauf, wie wichtig sie für gute Ergebnisse ist. Der Extremfall ist wahrscheinlich die Literatur; Menschen, die Literatur studieren, sagen selten etwas, das für die Produzenten von Nutzen wäre.
Obwohl die Situation in den Naturwissenschaften besser ist, ist die Überschneidung zwischen der Art von Arbeit, die man tun darf, und der Art von Arbeit, die gute Sprachen hervorbringt, bedauerlich gering. (Olin Shivers hat sich darüber eloquent beschwert.) Typen scheinen zum Beispiel eine unerschöpfliche Quelle für Forschungsarbeiten zu sein, obwohl statische Typisierung echte Makros auszuschließen scheint – ohne die meiner Meinung nach keine Sprache es wert ist, verwendet zu werden.
Der Trend geht nicht nur dahin, dass Sprachen als Open-Source-Projekte und nicht als „Forschung“ entwickelt werden, sondern auch dahin, dass Sprachen von den Anwendungsentwicklern entworfen werden, die sie verwenden müssen, und nicht von Compiler-Entwicklern. Das scheint ein guter Trend zu sein und ich erwarte, dass er sich fortsetzen wird.
Im Gegensatz zur Physik in hundert Jahren, die fast zwangsläufig unvorhersehbar ist, denke ich, dass es prinzipiell möglich ist, jetzt eine Sprache zu entwerfen, die Benutzer in hundert Jahren ansprechen würde.
Eine Möglichkeit, eine Sprache zu entwerfen, besteht darin, einfach das Programm aufzuschreiben, das man schreiben möchte, unabhängig davon, ob es einen Compiler gibt, der es übersetzen kann, oder eine Hardware, die es ausführen kann. Wenn man das tut, kann man unbegrenzte Ressourcen annehmen. Es scheint, dass wir uns heute ebenso unbegrenzte Ressourcen vorstellen können sollten wie in hundert Jahren.
Welches Programm möchte man schreiben? Was auch immer am wenigsten Arbeit macht. Außer nicht ganz: was auch immer am wenigsten Arbeit machen würde, wenn Ihre Ideen zur Programmierung nicht bereits von den Sprachen beeinflusst wären, die Sie derzeit gewohnt sind. Ein solcher Einfluss kann so allgegenwärtig sein, dass es große Anstrengungen erfordert, ihn zu überwinden. Man sollte meinen, es wäre für so faule Kreaturen wie uns offensichtlich, wie man ein Programm mit dem geringsten Aufwand ausdrückt. Tatsächlich sind unsere Vorstellungen davon, was möglich ist, oft so begrenzt, dass einfachere Formulierungen von Programmen sehr überraschend sind. Sie sind etwas, das man entdecken muss, nicht etwas, in das man natürlich versinkt.
Ein hilfreicher Trick hierbei ist, die Länge des Programms als Annäherung für den Arbeitsaufwand zu verwenden. Nicht die Länge in Zeichen, natürlich, sondern die Länge in unterschiedlichen syntaktischen Elementen – im Grunde die Größe des Parse-Baums. Es ist vielleicht nicht ganz richtig, dass das kürzeste Programm am wenigsten Arbeit macht, aber es ist nah genug dran, dass es besser ist, das solide Ziel der Kürze anzustreben als das verschwommene, nahegelegene Ziel des geringsten Aufwands. Dann wird der Algorithmus für das Sprachdesign: Schauen Sie sich ein Programm an und fragen Sie: Gibt es eine Möglichkeit, dies kürzer zu schreiben?
In der Praxis wird das Schreiben von Programmen in einer imaginären hundertjährigen Sprache in unterschiedlichem Maße funktionieren, je nachdem, wie nah Sie am Kern sind. Sortierroutinen können Sie jetzt schreiben. Aber es wäre schwierig, jetzt vorherzusagen, welche Art von Bibliotheken in hundert Jahren benötigt werden könnten. Vermutlich werden viele Bibliotheken für Domänen sein, die noch nicht einmal existieren. Wenn SETI@home funktioniert, brauchen wir zum Beispiel Bibliotheken für die Kommunikation mit Außerirdischen. Es sei denn natürlich, sie sind fortgeschritten genug, dass sie bereits in XML kommunizieren.
Am anderen Extrem denke ich, dass Sie den Kern der Sprache bereits heute entwerfen könnten. Tatsächlich würden einige argumentieren, dass er bereits 1958 weitgehend entworfen wurde.
Wenn die hundertjährige Sprache heute verfügbar wäre, würden wir in ihr programmieren wollen? Eine Möglichkeit, diese Frage zu beantworten, ist ein Rückblick. Wenn die heutigen Programmiersprachen 1960 verfügbar gewesen wären, hätten sie dann irgendjemand nutzen wollen?
In gewisser Weise ist die Antwort nein. Sprachen heute setzen eine Infrastruktur voraus, die 1960 nicht existierte. Zum Beispiel würde eine Sprache, bei der die Einrückung wichtig ist, wie Python, auf Terminaldruckern nicht sehr gut funktionieren. Aber wenn wir solche Probleme beiseite lassen – angenommen, zum Beispiel, dass Programme alle nur auf Papier geschrieben wurden –, hätten Programmierer der 1960er Jahre gerne Programme in den Sprachen geschrieben, die wir heute verwenden?
Ich denke schon. Einige der weniger fantasievollen, die Artefakte früherer Sprachen in ihre Vorstellungen von dem, was ein Programm ist, eingebaut hatten, hätten vielleicht Schwierigkeiten gehabt. (Wie kann man Daten manipulieren, ohne Zeigerarithmetik zu betreiben? Wie kann man Flussdiagramme ohne Gotos implementieren?) Aber ich denke, die klügsten Programmierer hätten keine Probleme gehabt, das Beste aus den heutigen Sprachen herauszuholen, wenn sie sie gehabt hätten.
Wenn wir die hundertjährige Sprache jetzt hätten, wäre sie zumindest ein großartiger Pseudocode. Was ist mit dem Schreiben von Software damit? Da die hundertjährige Sprache für einige Anwendungen schnellen Code generieren muss, könnte sie vermutlich Code generieren, der effizient genug ist, um auf unserer Hardware akzeptabel zu laufen. Wir müssten möglicherweise mehr Optimierungshinweise geben als Benutzer in hundert Jahren, aber es könnte immer noch ein Netto-Gewinn sein.
Jetzt haben wir zwei Ideen, die, wenn man sie kombiniert, interessante Möglichkeiten nahelegen: (1) die hundertjährige Sprache könnte prinzipiell heute entworfen werden, und (2) eine solche Sprache, wenn sie existierte, könnte heute gut zum Programmieren sein. Wenn man diese Ideen so darlegt, kann man nicht anders, als zu denken: Warum nicht versuchen, die hundertjährige Sprache jetzt zu schreiben?
Wenn man am Sprachdesign arbeitet, ist es meiner Meinung nach gut, ein solches Ziel zu haben und es bewusst im Auge zu behalten. Wenn man Autofahren lernt, lehrt man einen der Grundsätze, das Auto nicht auszurichten, indem man die Motorhaube mit den auf der Straße gemalten Streifen ausrichtet, sondern indem man auf einen Punkt in der Ferne zielt. Selbst wenn alles, was Sie interessiert, das ist, was in den nächsten zehn Fuß passiert, ist dies die richtige Antwort. Ich denke, wir können und sollten dasselbe mit Programmiersprachen tun.
Anmerkungen
Ich glaube, Lisp Machine Lisp war die erste Sprache, die das Prinzip verkörperte, dass Deklarationen (außer denen von dynamischen Variablen) lediglich Optimierungshinweise waren und die Bedeutung eines korrekten Programms nicht veränderten. Common Lisp scheint das erste zu sein, das dies explizit erklärt hat.
Danke an Trevor Blackwell, Robert Morris und Dan Giffin für das Lesen von Entwürfen davon, und an Guido van Rossum, Jeremy Hylton und den Rest der Python-Crew für die Einladung, auf der PyCon zu sprechen.