Tree Calculus
(treecalcul.us)- Ein System, das Berechnungen mit minimaler Syntax aufbauen will und mit nur einem Operator △ sowie bloßer Anwendung zugleich Minimalität, Turing-Vollständigkeit, Reflexivität und Modularität abdeckt
- Die Grammatik ist
E::= △ | E E; berechnet wird, wenn △ auf drei Werte wirkt, und die Werte sind natürliche binäre Bäume aus Blatt-, Stamm- und Gabelknoten - Die kombinatorische Logik K und S lassen sich innerhalb des Tree Calculus ausdrücken, wodurch er Turing-vollständig ist; anders als im λ-Kalkül können rekursive Funktionen in Normalform dargestellt werden
- Da auch Programme als Werte behandelt werden, sind Introspektion durch Selbstanwendung und Reflexion möglich; ein Beispiel ist, dass
size sizezu 168 ausgewertet wird - Unterterme treten als Unterbäume hervor, was zu Demos für das Bootstrapping gemeinsamer Funktionen, Serialisierung, Programmanalyse und -optimierung sowie statische und dynamische Typisierung führt
Natürliche binäre Bäume mit einem Operator
- Tree Calculus wurde von Barry Jay entdeckt; auf der Website sind sein Buch und Blog sowie von Johannes Bader entwickelte Demos verlinkt
- Die vier Kerneigenschaften werden als minimal, Turing-complete, reflective und modular zusammengefasst
-
Minimalität
- Tree Calculus hat nur einen Operator, △
- Die Grammatik hat die Form
E ::= △ | E E - Visuell ist △ ein Baumknoten; wird
E1aufE2angewendet, wirdE2rechts an die Wurzel vonE1angehängt - Die Werte sind natürliche binäre Bäume, deren Knoten leaf, stem und fork heißen
- Praktische Demos
- portability: zeigt, dass sich einfache und sichere Interpreter für mehrere Plattformen erstellen lassen
- emit-json: zeigt ein Beispiel, das sich für die plattformübergreifende Erzeugung von Konfigurationen eignet
Turing-Vollständigkeit und Reflexion
-
Turing-Vollständigkeit
- Die Operatoren K und S der kombinatorischen Logik lassen sich im Tree Calculus ausdrücken
K = △ △S x = △ (△ x)- Da die auf K/S basierende kombinatorische Logik vollständig ist, ist auch Tree Calculus Turing-vollständig
- Anders als im λ-Kalkül lassen sich rekursive Funktionen mit Fixpunktkonstruktionen wie orange/brown in Normalform darstellen
-
Reflexivität
triage {l, s, f} = △ (△ l s) fführt eine Fallunterscheidung für leaf, stem und fork durch- Die natürliche Zahl
nlässt sich als△^n △darstellen - Ein 0-Test wird als
triage {true, K false, K² false}konstruiert - Da auch Programme Werte sind, können intensionale Programme durch Selbstanwendung Introspektion und Reflexion durchführen
- Das Beispielprogramm
sizeberechnet die Knotenzahl seines Arguments, undsize sizewird zu 168 ausgewertet - Praktische Demos
- serialize-anything: behandelt die Möglichkeit der Programmserialisierung
- halting-problem: formuliert das Halteproblem einfacher
- fusion: drückt Programmanalyse und -optimierung als Funktionen aus
- gradual-typing: bietet ein Beispiel, das statische und dynamische Typisierung als Funktionsaufrufe behandelt
Modularität und Demos
- Unterterme werden als Unterbäume dargestellt
- Das oben auf der Seite gezeigte Programm
sizeverwendettriage, um Knoten rekursiv zu zählen - Praktische Demos
- bootstrap-basics: zeigt, dass sich gemeinsame Funktionen leicht bootstrappen lassen
- size-of-meaningful-programs: zeigt, dass leistungsfähige Programme nicht zwingend große Bäume sein müssen
1 Kommentare
Meinungen auf Hacker News
Tree Calculus ist ein spannendes Thema mit Implikationen, die über diese Website hinausgehen.
Schade ist allerdings, dass die Website Prof. Barry Jay, den Begründer und Autor, nicht ausdrücklich nennt. Wer mehr erfahren möchte, kann Jays Buch lesen: https://github.com/barry-jay-personal/tree-calculus/blob/mas...
Die Quellenangabe könnte klarer sein, und das wird auch noch verbessert, aber es gibt keinerlei Absicht, sich fremde Verdienste anzueignen. Mehr Hintergrund steht in dieser Antwort: https://news.ycombinator.com/item?id=42375914
Sieht cool aus, aber die Seite gibt viel zu wenig Orientierung, sodass es schwer zu verstehen ist.
Eine Erklärung „für Einsteiger“ wäre gut.
Der Unterschied zum SKI-Kalkül besteht darin, dass es die Struktur seiner eigenen Programme reflektieren kann, zum Beispiel um zu entscheiden, ob zwei Programme gleich sind: https://github.com/barry-jay-personal/tree-calculus/blob/mas...
Außerdem konvergiert ein Programm bei Anwendung der vorgegebenen Reduktionsregeln, anders als beim Lambda-Kalkül, gegen eine stabile Normalform; Fälle, in denen man in eine unendliche Reduktionskette geraten kann, werden vermieden: https://treecalcul.us/specification/, https://sci-hub.se/https://dl.acm.org/doi/abs/10.1016/j.tcs....
Dadurch ist Reflexion möglich, ohne Programme zu quoten oder zu serialisieren und auf stabile Datenstrukturen auszuweichen; es hat also gewisse Ähnlichkeiten mit der Homoikonizität von Lisp.
Sie verwendet einen Ein-Wort-Titel, leicht buzzwordartige Formulierungen und animierte Codebeispiele wie Websites trendiger Programmiersprachen oder Frameworks, während der Fließtext übermäßig dicht und lang im akademischen Stil gehalten ist. Zugleich enthält selbst dieser akademische Stil nicht genug Details, um zu verstehen, was eigentlich passiert.
Ich habe eine ganze Weile versucht, die Absätze zu parsen, aber trotz der Weitschweifigkeit sagt die Seite im Grunde nur, wie bei einer normalen Landingpage für eine Programmiersprache, „was der Autor an dieser Sprache gut findet“, ohne zu erklären, wie sie funktioniert. Am Ende muss man wohl doch die Spezifikation lesen.
E ::= t | E Ein der Spezifikation verleitet beim ersten Lesen leicht zu der falschen Annahme, alle Ausdrücke sähen einfach wiet t t t t t taus.Tatsächlich muss die Klammerstruktur erhalten bleiben, sodass Formen wie
(t t) (t ((t t) (t t)))entstehen; auf der obersten Ebene und innerhalb jeder Klammer gibt es immer genau zwei Teilausdrücke. Das Leerzeichen verhält sich also wie ein binärer Operator.Da dieser Ausdruck viele Klammern enthält, betrachtet man diesen binären Operator als linksassoziativ.
a b cwird als(a b) cinterpretiert,a b c dals((a b) c) d.So erkennt man, woher der Baum kommt. Da es nur ein einziges Blattsymbol
tgibt, beginnen alle Ausdrücke nach Entfernen unnötiger Klammern immer mitt, gefolgt von mehreren Ausdrücken. Man zeichnet das erstetals Knoten und zeichnet dann für jeden folgenden Ausdruck nach demselben Verfahren einen Teilbaum.Die semantischen Regeln auf der Spezifikationsseite beschreiben, wie man Knoten mit drei oder mehr Teilbäumen „vereinfacht“, also wie man Ausdrücke reduziert, bei denen auf
tdrei oder mehr Teilausdrücke folgen.Wikipedia-Artikel beginnen ja meist mit Sätzen wie „Lambda-Kalkül ist ein formales System für …“ oder „Matrix calculus ist eine spezielle Notation für …“.
Ein unbeschrifteter Baum ist eine baumförmige Datenstruktur, deren Knoten keine Daten tragen; die Reihenfolge der Kinder ist jedoch vorhanden. Tree Calculus definiert eine Menge von Regeln, die unbeschriftete Bäume auswerten und daraus andere unbeschriftete Bäume erzeugen.
Wendet man die Regeln wiederholt an, gerät man entweder in eine Endlosschleife oder erreicht einen Baum, der sich nicht mehr verändert. Die Regeln sind so entworfen, dass sie binäre Bäume nicht beeinflussen; wertet man einen binären Baum aus, kommt derselbe Baum heraus, und die Berechnung ist beendet.
Diese Regeln sind auf der Seite „Specification“ in der in der Programmiersprachentheorie üblichen Form einer Small-Step-Semantik notiert.
Die Behauptung lautet, dass die Auswertungsregeln Turing-vollständig sind, also jede Berechnung ausdrücken können, und dass die Auswertung asymptotisch optimal ist, sodass Programme jeder Sprache in Tree Calculus mit nahezu konstantem Overhead ausgeführt werden können. Auf den ersten Blick ist das keine abwegige Behauptung, aber wie wichtig das praktisch ist, ist unklar.
Als Anwendungsgebiet ist es für einige Forschende in der Programmiersprachentheorie interessant und könnte vielleicht dazu dienen, Beweise in der Berechnungstheorie zu vereinfachen. Wenn dich so etwas interessiert, würde ich empfehlen, zuerst den Lambda-Kalkül zu lernen, der einfacher, bekannter und nützlicher ist als Tree Calculus.
Auf der Homepage stehen „Democratizing Functions“ und „Democratizing Metatheory“; unabhängig davon, was gemeint ist, wirkt es stark so, als werde das Wort democratizing überstrapaziert
Auch die zweite Definition von Britannica lautet „etwas für alle verfügbar machen, für alle verständlich machen“: https://www.britannica.com/dictionary/democratize
„Sprache wird von Kultur geformt, und du bist Teil dieser Kultur. Du musst die Verantwortung nicht abgeben. Es gibt Wahlmöglichkeiten“
Ich habe selbst eine Zeichnung erstellt, um die Logik der Reduktionsregeln des Tree Calculus „intuitiv“ zu verstehen: https://latypoff.com/tree-calculus-visualized/
Für visuell denkende Menschen könnte das hilfreich sein
Allerdings scheint im zweiten Bild „Stem with a single leaf child“ ein Fehler zu sein. Die Linie, die vom Dreieck nach unten führt, geht zu einem Quadrat; dieses Quadrat müsste wohl ein Kreis sein
Ich frage mich, ob die Leute, die das empfohlen haben, wirklich verstanden haben, was es ist, und es deshalb empfohlen haben
Kann jemand erklären, warum das nicht einfach Lisp oder Forth mit anderer Syntax ist?
Ich will es nicht kritisieren oder oberflächlich abtun, sondern es wirklich verstehen
Da diese Fähigkeit nützlich ist, wurden Lisp-Sprachen Dinge wie Makros hinzugefügt, und auch die Implementierungsweisen unterscheiden sich. Selbst das in Lisp-Sprachen verbreitete
evalist kein Teil des Lambda-Kalküls. Im Lambda-Kalkül gibt es nur Abstraktion, Anwendung und Variablen, aber keine UmgebungWenn der Begriff der Reflexion gut definiert ist und Tree Calculus reflexiv ist, dann ist es sicher nicht nur Lisp mit anderer Syntax, und Forth erst recht nicht
Ich bin kein Experte, also bitte mit Vorsicht genießen. Praktisch kann es wie ein langsames Lisp aussehen, theoretisch aber unterscheidet es sich vom Lambda-Kalkül und kann als Grundlage dienen, um etwas wie ein langsames Lisp einfacher zu implementieren
Andere Vorlieben sind natürlich völlig in Ordnung, aber es ist schade, dass Homoikonizität größtenteils in Lisp-Dialekten eingeschlossen ist
Ich habe den Z-Kombinator aus SKI über ein Beispiel im Lambda-Kalkül in Tree Calculus umgewandelt und als Baum ausgegeben
Getestet habe ich es nicht, aber der Ursprung ist unoptimierter Code, der mit einem Tool konvertiert wurde. Hintergrund dazu siehe den Artikel über Fixpunktkombinatoren: https://en.wikipedia.org/wiki/Fixed-point_combinator
Z = \f. (\x. f (\v. x x v)) (\x. f (\v. x x v))schreiben und auch in SKI kürzer ausdrückenEs freut mich zu sehen, dass Johannes mit Tree Calculus experimentiert und Möglichkeiten explizit zeigt, die in meinem Buch GitHub.com/barry-jay-personal/tree-calculus/tree_book.pdf nur implizit vorhanden waren
Endlich gibt es einen typisierten Tree Calculus, und deshalb habe ich angefangen, auf GitHub.com/barry-jay-personal zu bloggen
Man muss rechts den Download-Button finden
Ich habe mir das eine ganze Weile angesehen und ein paar Dinge erkannt. Das könnte besonders Leuten helfen, die mit dem Lambda-Kalkül oder formaler Semantik einigermaßen vertraut sind, einen Einstieg zu finden
Um zu verstehen, was Small-Step-Semantik bedeutet, musste ich bis zur OCaml-Implementierung hinuntergehen, weil die grundlegende Baumstruktur nicht gut zu erkennen war. Bei den vier Reduktionsformeln der Definition sieht man, was worauf angewendet wird, wenn man bei den ersten drei Termen Klammern setzt. Auch rechts scheinen Klammern zu fehlen
Zum Beispiel sollte man es eher so lesen:
(t (t) a) b -> a,(t (t a) b) c -> (a c) (b c),(t (t a b) c) t -> a,(t (t a b) c) (t u) -> b u,(t (t a b) c) (t u v) -> (c u) vAußerdem fehlen in der Tabelle Fälle, die offenbar „selbstverständlich“ aus der Assoziativität der Syntax folgen; wenn man
t a -> (t a),(t a) b -> (t a b)ergänzt, lässt sich die Bedeutungsreduktion sauberer auf Ausdrücke der GrammatikE EanwendenDer Kern ist: So wie man im Lambda-Kalkül Lambdas bündelt, um eine von zwei Optionen „auswählen“ zu lassen, ist dieser Tree Calculus so gebaut, dass er je nachdem, ob ein gegebener Knoten ein Blatt, ein Stamm oder eine Verzweigung ist, drei Auswahlmöglichkeiten hat. Das ist der Kern der Regeln 3a, 3b und 3c, und die übrigen Funktionen des Systems bauen auf dieser dreifachen Verzweigung auf
Dadurch wirkt es zwar wie ein interessantes Kalkül, aber ob es sich besser als SKI oder Lambda-Kalkül für De-Kompilierung, Serialisierung und Kompilierung eignet, ist eine andere Frage. De-Kompilierung ist schwierig, Serialisierung ist einfach, und Kompilierung ist einigermaßen einfach
In Python kann man Leaf als leere Liste, Stem als Liste mit einem einzelnen Element und Fork als Liste mit zwei Elementen darstellen und
applyentsprechend dem OCaml-Code der Spezifikation implementieren.Wenn man
false,trueundnotals Bäume definiert, funktionierennot false -> trueundnot true -> false.Leafkannnull,StemlistundForkconssein; mitapply t-not t-falseundapply t-not t-truelässt sich dasselbe Ergebnis überprüfen.