Loading...

PRÄGNANZ IST MACHT

Original

Mai 2002

"Die Menge an Bedeutung, die in einem kleinen Raum durch algebraische Zeichen komprimiert wird, ist ein weiterer Umstand, der die Schlussfolgerungen erleichtert, die wir gewohnt sind, mit ihrer Hilfe durchzuführen."

  • Charles Babbage, zitiert in Iversons Turing Award Lecture

In der Diskussion über die von Revenge of the Nerds auf der LL1-Mailingliste aufgeworfenen Probleme schrieb Paul Prescod etwas, das mir im Gedächtnis blieb.

Pythons Ziel ist Regelmäßigkeit und Lesbarkeit, nicht Prägnanz.

Auf den ersten Blick scheint dies eine ziemlich verdammende Aussage über eine Programmiersprache zu sein. Soweit ich das beurteilen kann, gilt Prägnanz = Macht. Wenn das so ist, dann erhalten wir durch Substitution:

Pythons Ziel ist Regelmäßigkeit und Lesbarkeit, nicht Macht.

und dies scheint kein Kompromiss (wenn es denn ein Kompromiss ist) zu sein, den man eingehen möchte. Es ist nicht weit davon entfernt zu sagen, dass Pythons Ziel nicht darin besteht, als Programmiersprache effektiv zu sein.

Gilt Prägnanz = Macht? Dies scheint mir eine wichtige Frage zu sein, vielleicht die wichtigste Frage für jeden, der sich für Sprachdesign interessiert, und eine, der man sich direkt stellen sollte. Ich bin mir noch nicht sicher, ob die Antwort ein einfaches Ja ist, aber es scheint eine gute Hypothese zu sein, mit der man beginnen kann.

Hypothese

Meine Hypothese ist, dass Prägnanz Macht ist oder zumindest so nah dran ist, dass man sie außer in pathologischen Beispielen als identisch betrachten kann.

Es scheint mir, dass Prägnanz das ist, wofür Programmiersprachen da sind. Computer wären genauso glücklich, wenn man ihnen sagen würde, was sie direkt in Maschinensprache tun sollen. Ich denke, der Hauptgrund, warum wir uns die Mühe machen, Hochsprachen zu entwickeln, ist, um Hebelwirkung zu erzielen, so dass wir in 10 Zeilen einer Hochsprache sagen (und noch wichtiger, denken) können, was 1000 Zeilen Maschinensprache erfordern würde. Mit anderen Worten, der Hauptzweck von Hochsprachen ist es, den Quellcode zu verkleinern.

Wenn kleinerer Quellcode der Zweck von Hochsprachen ist und die Macht von etwas darin besteht, wie gut es seinen Zweck erfüllt, dann ist das Maß für die Macht einer Programmiersprache, wie klein sie Ihre Programme macht.

Umgekehrt ist eine Sprache, die Ihre Programme nicht klein macht, schlecht darin, was Programmiersprachen tun sollen, wie ein Messer, das nicht gut schneidet, oder ein Druck, der unleserlich ist.

Metriken

Klein in welchem Sinne? Das gebräuchlichste Maß für die Codegröße ist die Anzahl der Codezeilen. Aber ich denke, dass diese Metrik die gebräuchlichste ist, weil sie am einfachsten zu messen ist. Ich glaube nicht, dass jemand wirklich glaubt, dass sie der wahre Test für die Länge eines Programms ist. Unterschiedliche Sprachen haben unterschiedliche Konventionen, wie viel man in eine Zeile schreiben sollte; in C haben viele Zeilen nichts anderes als ein oder zwei Trennzeichen.

Ein weiterer einfacher Test ist die Anzahl der Zeichen in einem Programm, aber auch das ist nicht sehr gut; einige Sprachen (z. B. Perl) verwenden einfach kürzere Bezeichner als andere.

Ich denke, ein besseres Maß für die Größe eines Programms wäre die Anzahl der Elemente, wobei ein Element alles ist, was ein separater Knoten wäre, wenn man einen Baum zeichnet, der den Quellcode darstellt. Der Name einer Variablen oder Funktion ist ein Element; eine ganze Zahl oder eine Gleitkommazahl ist ein Element; ein Segment von Literaltext ist ein Element; ein Element eines Musters oder eine Formatdirektive ist ein Element; ein neuer Block ist ein Element. Es gibt Grenzfälle (ist -5 zwei Elemente oder eins?) aber ich denke, die meisten sind für jede Sprache gleich, so dass sie Vergleiche nicht stark beeinflussen.

Diese Metrik muss noch ausgearbeitet werden, und sie könnte im Fall bestimmter Sprachen Interpretation erfordern, aber ich denke, sie versucht, das Richtige zu messen, nämlich die Anzahl der Teile, aus denen ein Programm besteht. Ich denke, der Baum, den man in dieser Übung zeichnen würde, ist das, was man im Kopf machen muss, um sich das Programm vorzustellen, und so ist seine Größe proportional zu der Menge an Arbeit, die man leisten muss, um es zu schreiben oder zu lesen.

Design

Diese Art von Metrik würde es uns ermöglichen, verschiedene Sprachen zu vergleichen, aber das ist, zumindest für mich, nicht ihr Hauptwert. Der Hauptwert des Prägnanztests ist als Leitfaden beim Entwurf von Sprachen. Der nützlichste Vergleich zwischen Sprachen ist der zwischen zwei potenziellen Varianten derselben Sprache. Was kann ich in der Sprache tun, um Programme kürzer zu machen?

Wenn die konzeptionelle Belastung eines Programms proportional zu seiner Komplexität ist und ein bestimmter Programmierer eine feste konzeptionelle Belastung tolerieren kann, dann ist dies dasselbe wie die Frage, was kann ich tun, um Programmierern zu ermöglichen, das Meiste zu erreichen? Und das scheint mir identisch mit der Frage, wie kann ich eine gute Sprache entwerfen?

(Übrigens, nichts macht es offensichtlicher, dass das alte Klischee "alle Sprachen sind gleichwertig" falsch ist, als das Entwerfen von Sprachen. Wenn man eine neue Sprache entwirft, vergleicht man ständig zwei Sprachen - die Sprache, wenn ich x tun würde, und wenn ich es nicht tun würde - um zu entscheiden, welche besser ist. Wenn dies wirklich eine bedeutungslose Frage wäre, könnte man genauso gut eine Münze werfen.)

Das Streben nach Prägnanz scheint ein guter Weg zu sein, um neue Ideen zu finden. Wenn man etwas tun kann, das viele verschiedene Programme kürzer macht, ist es wahrscheinlich kein Zufall: Man hat wahrscheinlich eine nützliche neue Abstraktion entdeckt. Man könnte sogar ein Programm schreiben, das hilft, indem es Quellcode nach sich wiederholenden Mustern durchsucht. Unter anderen Sprachen wären diejenigen, die einen Ruf für Prägnanz haben, diejenigen, die man nach neuen Ideen suchen sollte: Forth, Joy, Icon.

Vergleich

Die erste Person, die über diese Themen schrieb, soweit ich weiß, war Fred Brooks im Mythical Man Month. Er schrieb dass Programmierer etwa die gleiche Menge an Code pro Tag zu generieren schienen, unabhängig von der Sprache. Als ich das zum ersten Mal in meinen frühen Zwanzigern las, war es eine große Überraschung für mich und schien enorme Auswirkungen zu haben. Es bedeutete, dass (a) die einzige Möglichkeit, Software schneller zu schreiben, darin bestand, eine prägnantere Sprache zu verwenden, und (b) jemand, der sich die Mühe machte, dies zu tun, Konkurrenten, die dies nicht taten, im Staub zurücklassen konnte.

Brooks' Hypothese, wenn sie stimmt, scheint im Herzen des Hackens zu liegen. In den Jahren seitdem habe ich jede Beweislage, die ich bekommen konnte, zu dieser Frage genau beobachtet, von formalen Studien bis hin zu Anekdoten über einzelne Projekte. Ich habe nichts gesehen, das ihn widerlegt.

Ich habe noch keine Beweise gesehen, die mir schlüssig erschienen, und ich erwarte auch keine. Studien wie Lutz Prechelt's Vergleich von Programmiersprachen, obwohl sie die Art von Ergebnissen hervorbringen, die ich erwartet habe, neigen dazu, Probleme zu verwenden, die zu kurz sind, um aussagekräftige Tests zu sein. Ein besserer Test für eine Sprache ist das, was in Programmen passiert, die einen Monat lang geschrieben werden. Und der einzige wirkliche Test, wenn man wie ich glaubt, dass der Hauptzweck einer Sprache darin besteht, gut zum Denken zu sein (und nicht nur, um einem Computer zu sagen, was er tun soll, wenn man es sich ausgedacht hat), ist, welche neuen Dinge man damit schreiben kann. Also ist jeder Sprachvergleich, bei dem man eine vorgegebene Spezifikation erfüllen muss, ein leicht falsches Ding.

Der wahre Test einer Sprache ist, wie gut man neue Probleme entdecken und lösen kann, nicht wie gut man sie verwenden kann, um ein Problem zu lösen, das jemand anderes bereits formuliert hat. Diese beiden Kriterien sind sehr unterschiedlich. In der Kunst funktionieren Medien wie Stickerei und Mosaik gut, wenn man im Voraus weiß, was man machen möchte, aber sie sind absolut schlecht, wenn man es nicht tut. Wenn man das Bild beim Erstellen entdecken möchte - wie man es bei allem tun muss, was so komplex ist wie ein Bild einer Person, zum Beispiel - muss man ein flüssigeres Medium wie Bleistift oder Tusche oder Öl verwenden. Und tatsächlich wird die Herstellung von Wandteppichen und Mosaiken in der Praxis so gemacht, dass man zuerst ein Gemälde anfertigt und es dann kopiert. (Das Wort "Cartoon" wurde ursprünglich verwendet, um ein Gemälde zu beschreiben, das für diesen Zweck bestimmt war).

Das bedeutet, dass wir wahrscheinlich nie genaue Vergleiche der relativen Macht von Programmiersprachen haben werden. Wir werden präzise Vergleiche haben, aber keine genauen. Insbesondere explizite Studien zum Zweck des Vergleichs von Sprachen, weil sie wahrscheinlich kleine Probleme verwenden werden und notwendigerweise vorgegebene Probleme verwenden werden, werden dazu neigen, die Macht der mächtigeren Sprachen zu unterschätzen.

Berichte aus dem Feld, obwohl sie notwendigerweise weniger präzise sein werden als "wissenschaftliche" Studien, sind wahrscheinlich aussagekräftiger. Zum Beispiel machte Ulf Wiger von Ericsson eine Studie, die zu dem Schluss kam, dass Erlang 4-10x prägnanter war als C++ und die Softwareentwicklung proportional schneller war:

Vergleiche zwischen Ericsson-internen Entwicklungsprojekten zeigen ähnliche Zeilen/Stunden-Produktivität, einschließlich aller Phasen der Softwareentwicklung, eher unabhängig davon, welche Sprache (Erlang, PLEX, C, C++ oder Java) verwendet wurde. Was die verschiedenen Sprachen dann unterscheidet, ist das Quellcode- Volumen.

Die Studie befasst sich auch explizit mit einem Punkt, der nur implizit in Brooks' Buch enthalten war (da er Zeilen von debuggtem Code maß): Programme, die in mächtigeren Sprachen geschrieben werden, neigen dazu, weniger Fehler zu haben. Das wird zu einem Selbstzweck, der möglicherweise wichtiger ist als die Programmiererproduktivität, in Anwendungen wie Netzwerkschaltern.

Der Geschmackstest

Letztendlich denke ich, dass man seinem Bauchgefühl folgen muss. Wie fühlt es sich an, in der Sprache zu programmieren? Ich denke, der Weg, um die beste Sprache zu finden (oder zu entwerfen), ist, hypersensibler zu werden, wie gut eine Sprache es einem ermöglicht zu denken, und dann die Sprache zu wählen/zu entwerfen, die sich am besten anfühlt. Wenn ein Sprachmerkmal unbeholfen oder einschränkend ist, keine Sorge, Sie werden es merken.

Solche Hypersensibilität wird mit einem Preis verbunden sein. Sie werden feststellen, dass Sie es nicht ertragen können, in ungeschickten Sprachen zu programmieren. Ich finde es unerträglich einschränkend, in Sprachen ohne Makros zu programmieren, genauso wie jemand, der an dynamische Typisierung gewöhnt ist, es unerträglich einschränkend findet, zu einer Programmierung in einer Sprache zurückkehren zu müssen, in der man den Typ jeder Variablen deklarieren muss und keine Liste von Objekten unterschiedlicher Typen erstellen kann.

Ich bin nicht der Einzige. Ich kenne viele Lisp-Hacker, denen das passiert ist. Tatsächlich könnte das genaueste Maß für die relative Macht von Programmiersprachen der Prozentsatz der Leute sein, die die Sprache kennen, die jeden Job annehmen würden, bei dem sie diese Sprache verwenden dürfen, unabhängig vom Anwendungsbereich.

Einschränkend

Ich denke, die meisten Hacker wissen, was es bedeutet, dass sich eine Sprache einschränkend anfühlt. Was passiert, wenn man das Gefühl hat? Ich denke, es ist das gleiche Gefühl, das man hat, wenn die Straße, die man nehmen möchte, gesperrt ist und man einen langen Umweg machen muss, um dorthin zu gelangen, wo man hin wollte. Es gibt etwas, das man sagen möchte, und die Sprache lässt es nicht zu.

Was hier wirklich passiert, denke ich, ist, dass eine einschränkende Sprache eine ist, die nicht prägnant genug ist. Das Problem ist nicht einfach, dass man nicht sagen kann, was man vorhatte. Es ist, dass der Umweg, den die Sprache einen machen lässt, länger ist. Versuchen Sie dieses Gedankenexperiment. Nehmen Sie an, es gäbe ein Programm, das Sie schreiben wollten, und die Sprache würde es Ihnen nicht erlauben, es so auszudrücken, wie Sie es geplant hatten, sondern würde Sie stattdessen zwingen, das Programm auf eine andere Weise zu schreiben, die kürzer war. Für mich zumindest würde sich das nicht sehr einschränkend anfühlen. Es wäre, als wäre die Straße, die Sie nehmen wollten, gesperrt, und der Polizist an der Kreuzung würde Sie zu einer Abkürzung statt zu einem Umweg leiten. Großartig!

Ich denke, der größte Teil (neunzig Prozent?) des Gefühls der Einschränkung kommt daher, dass man gezwungen ist, das Programm, das man in der Sprache schreibt, länger zu machen als das, das man im Kopf hat. Einschränkung ist größtenteils Mangel an Prägnanz. Wenn sich eine Sprache also einschränkend anfühlt, bedeutet das (meistens), dass sie nicht prägnant genug ist, und wenn eine Sprache nicht prägnant ist, wird sie sich einschränkend anfühlen.

Lesbarkeit

Das Zitat, mit dem ich begann, erwähnt zwei weitere Eigenschaften, Regelmäßigkeit und Lesbarkeit. Ich bin mir nicht sicher, was Regelmäßigkeit ist oder welchen Vorteil, wenn überhaupt, Code hat, der regelmäßig und lesbar ist, gegenüber Code, der nur lesbar ist. Aber ich denke, ich weiß, was mit Lesbarkeit gemeint ist, und ich denke, dass sie auch mit Prägnanz zusammenhängt.

Wir müssen hier vorsichtig sein, zwischen der Lesbarkeit einer einzelnen Codezeile und der Lesbarkeit des gesamten Programms zu unterscheiden. Es ist die zweite, die zählt. Ich stimme zu, dass eine Zeile Basic wahrscheinlich lesbarer ist als eine Zeile Lisp. Aber ein Programm, das in Basic geschrieben ist, wird mehr Zeilen haben als dasselbe Programm, das in Lisp geschrieben ist (besonders wenn man in Greenspunland übergeht). Die Gesamtanstrengung, das Basic-Programm zu lesen, wird sicherlich größer sein.

Gesamtaufwand = Aufwand pro Zeile x Anzahl der Zeilen

Ich bin mir nicht so sicher, ob Lesbarkeit direkt proportional zu Prägnanz ist, wie ich es bei Macht bin, aber sicherlich ist Prägnanz ein Faktor (im mathematischen Sinne; siehe Gleichung oben) in der Lesbarkeit. Es ist also vielleicht nicht einmal sinnvoll zu sagen, dass das Ziel einer Sprache Lesbarkeit ist, nicht Prägnanz; es könnte sein, wie zu sagen, dass das Ziel Lesbarkeit war, nicht Lesbarkeit.

Was Lesbarkeit pro Zeile bedeutet, für den Benutzer, der zum ersten Mal mit der Sprache in Berührung kommt, ist, dass der Quellcode ungefährlich aussieht. Also Lesbarkeit pro Zeile könnte eine gute Marketingentscheidung sein, auch wenn es eine schlechte Design- Entscheidung ist. Es ist isomorph zu der sehr erfolgreichen Technik, die es den Leuten ermöglicht, in Raten zu zahlen: Anstatt sie mit einem hohen Vorauszahlungspreis zu erschrecken, sagt man ihnen die niedrige monatliche Zahlung. Ratenzahlungen sind jedoch ein Verlust für den Käufer, so wie es die bloße Lesbarkeit pro Zeile wahrscheinlich für den Programmierer ist. Der Käufer wird viele dieser niedrigen, niedrigen Zahlungen leisten; und der Programmierer wird viele dieser einzeln lesbaren Zeilen lesen.

Dieser Kompromiss ist älter als Programmiersprachen. Wenn man es gewohnt ist, Romane und Zeitungsartikel zu lesen, kann die erste Erfahrung mit dem Lesen eines Mathematikpapiers entmutigend sein. Es kann eine halbe Stunde dauern, um eine einzige Seite zu lesen. Und dennoch bin ich mir ziemlich sicher, dass die Notation nicht das Problem ist, auch wenn es sich so anfühlen mag. Das Mathematikpapier ist schwer zu lesen, weil die Ideen schwer sind. Wenn man die gleichen Ideen in Prosa ausdrücken würde (wie Mathematiker es tun mussten, bevor sie prägnante Notationen entwickelten), wären sie nicht einfacher zu lesen, weil das Papier die Größe eines Buches annehmen würde.

In welchem Umfang?

Eine Reihe von Leuten haben die Idee abgelehnt, dass Prägnanz = Macht. Ich denke, es wäre nützlicher, anstatt einfach zu argumentieren, dass sie gleich sind oder nicht, zu fragen: in welchem Umfang gilt Prägnanz = Macht? Denn Prägnanz ist eindeutig ein großer Teil dessen, wofür Hochsprachen da sind. Wenn es nicht alles ist, wofür sie da sind, dann wofür sind sie sonst noch da, und wie wichtig sind diese anderen Funktionen im Verhältnis zueinander?

Ich schlage dies nicht nur vor, um die Debatte zivilisierter zu gestalten. Ich möchte wirklich die Antwort wissen. Wann, wenn überhaupt, ist eine Sprache zu prägnant für ihr eigenes Wohl?

Die Hypothese, mit der ich begann, war, dass ich Prägnanz außer in pathologischen Beispielen als identisch mit Macht betrachten könnte. Was ich meinte, war, dass sie in jeder Sprache, die jemand entwerfen würde, identisch wären, aber dass, wenn jemand eine Sprache entwerfen wollte, um diese Hypothese explizit zu widerlegen, er das wahrscheinlich tun könnte. Ich bin mir dessen nicht einmal sicher, eigentlich.

Sprachen, nicht Programme

Wir sollten uns darüber im Klaren sein, dass wir über die Prägnanz von Sprachen sprechen, nicht von einzelnen Programmen. Es ist sicherlich möglich, dass einzelne Programme zu dicht geschrieben werden.

Darüber habe ich in On Lisp geschrieben. Ein komplexes Makro muss möglicherweise ein Vielfaches seiner eigenen Länge einsparen, um gerechtfertigt zu sein. Wenn das Schreiben eines haarigen Makros Ihnen jedes Mal, wenn Sie es verwenden, zehn Zeilen Code sparen könnte, und das Makro selbst zehn Zeilen Code lang ist, dann erhalten Sie eine Nettoersparnis in Zeilen, wenn Sie es mehr als einmal verwenden. Aber das könnte immer noch ein schlechter Schachzug sein, weil Makrodefinitionen schwieriger zu lesen sind als normaler Code. Sie müssen das Makro möglicherweise zehn oder zwanzig Mal verwenden, bevor es eine Netto- Verbesserung der Lesbarkeit erzielt.

Ich bin sicher, dass jede Sprache solche Kompromisse hat (obwohl ich vermute, dass die Einsätze höher werden, je mächtiger die Sprache wird). Jeder Programmierer muss Code gesehen haben, den eine clevere Person durch den Einsatz fragwürdiger Programmiertricks marginal kürzer gemacht hat.

Darüber gibt es also keine Diskussion - zumindest nicht von mir. Einzelne Programme können sicherlich zu prägnant für ihr eigenes Wohl sein. Die Frage ist, kann eine Sprache das sein? Kann eine Sprache Programmierer zwingen, Code zu schreiben, der kurz (in Elementen) ist, auf Kosten der Gesamt- Lesbarkeit?

Ein Grund, warum es schwer vorstellbar ist, dass eine Sprache zu prägnant ist, ist, dass, wenn es eine übermäßig kompakte Möglichkeit gäbe, etwas auszudrücken, es wahrscheinlich auch eine längere Möglichkeit gäbe. Wenn Sie zum Beispiel der Meinung wären, dass Lisp-Programme, die viele Makros oder höherwertige Funktionen verwenden, zu dicht sind, könnten Sie, wenn Sie es vorziehen würden, Code schreiben, der isomorph zu Pascal ist. Wenn Sie die Fakultät nicht in Arc als Aufruf einer höherwertigen Funktion ausdrücken möchten


(rec zero 1 * 1-)

können Sie auch eine rekursive Definition schreiben:


(rfn fact (x) (if (zero x) 1 (* x (fact (1- x)))))

Obwohl ich mir spontan keine Beispiele einfallen lassen kann, interessiere ich mich für die Frage, ob eine Sprache zu prägnant sein könnte. Gibt es Sprachen, die einen zwingen, Code auf eine Weise zu schreiben, die verkrampft und unverständlich ist? Wenn jemand Beispiele hat, wäre ich sehr daran interessiert, sie zu sehen.

(Erinnerung: Was ich suche, sind Programme, die sehr dicht sind nach der oben skizzierten Metrik "Elemente", nicht nur Programme, die kurz sind, weil Trennzeichen weggelassen werden können und alles einen einbuchstabigen Namen hat.)