 Hallo, dies ist ein Talk über die Erstellung eigener Programmiersprachen, die nicht miss sind. Und Sprachen, die nicht miss sind, sind die ersten, die wir lernen wollen. Für mich war das die erste Programmiersprache, die auf diesem Computer lief. Auf dem Apple 2e, den ich habe, den mir mein Vater in 1983 gegeben hat. Ich habe ihn heute mal angemacht. Er funktioniert immer noch. Und darauf läuft eine Programmiersprache namens Appesoft Basic. Und dafür kann man immer noch Dokumentationen finden. Zum Beispiel gibt es da eine Einführungsanleitung und da steht drin, wie ein einfaches Programm aussieht. Man kann alle Zeilen sehen, zu jeder Zeile gibt es eine Nummer und jede Nummer hat dann einen Kommando. Es gibt keine Klassen und Funktionen, sondern es ist wirklich nur macht dies, macht das, springt jetzt zu dieser Zeile. Und es war viel Spaß damals. So, wir benutzen jetzt also das Racket System, um das zu implementieren. Und Racket ist großartig, es ist eine komplette Werkzeugkasten, um sich seine eigenen Sprachen zu erzeugen. Es hat Unterstützung für viele Sprachen. Und es ... ... spielt mit dem Code, für das Talk yourself. Ich habe alles auf den Top und eine Repositorie. Und wenn du mit dem Code spielen möchtest, ist ein Download Link verfügbar. Und auch mit allem Code. Ich muss Sie warnen, dieser Talk ist sehr heavy in Programmier-Code. Ich werde also vor euch eine Menge Code schreiben. Und wenn das einfach zu viel für euch ist, ist das okay. Und es ist kein Problem, wenn ihr einfach geht und euch einen anderen Talk anguckt. Aber wenn ihr euch jetzt gerne ein bisschen ernsthaft das Hacking angucken wollt, dann wäre das vielleicht ein Talk für euch. Also, Racket, wenn man es startet, kommt als erstes mit einem leeren Bildschirm, so wie diesem. Jeder Datei fängt an mit diesen rauten, langen Zeilen, die einem zeigt, in welcher Sprache diese Datei geschrieben ist. Und das System unterstützt viele Sprachen und eine davon wird auch Racket genannt. Racket ist eine OK Sprache, aber hat auch ein paar nervige Aspekte. Wir können hier jetzt mal was ausgeben, indem wir die Displayfunktion aufrufen. Und wenn wir das Programm jetzt ausführen, sehen wir das Output. Aber manchmal mal vielleicht auch mehrere Dinge anzeigen, wie zum Beispiel ein Erklärtext zusammen mit einer Berechnung. So, das hier wäre jetzt in der normalen Racket Sprache. Es ist halt ein List-Dialekt, wo überall immer Klammern benutzt werden und wo der Operator immer vor dem Rest steht. Das heißt, man schreibt nicht 1 plus 1, sondern man schreibt die Summe von 1 und 1. So, hoffentlich wisst ihr, was das jetzt geben soll, aber wir kriegen erst mal eine Fehlermeldung. Aber Display kriegt nur ein Argument an. Das zweite Argument ist ein Port, wo die Ausgabe hin soll. Also, wenn wir mehrere Teile im Output haben wollen, dann müssen wir mehrere Calls eingeben. So, jetzt haben wir das. Das ist aber natürlich super nervig. Und selbst Episode Basic hatte eine Option, wo du ein Print sagen konntest und dann einfach mehrere Dinge nacheinander ausgeben. So, aber wenn wir das versuchen jetzt hier, geht es nicht. So, das ist einfach nicht eingebaut. Also werden wir das jetzt einfach mal selber definieren. Um das zu machen, schreiben wir ein Makro. Und das heißt, dass wir diese Form mit dem Print am Anfang, das wird übersetzt werden in einen Code, den Racket schon kennt. Das heißt, wir wollen das übersetzen in diese zwei Calls zu Display. Und eine Beschränkung, die wir bedenken müssen, ist, dass eine Form immer nur in eine andere Form übersetzt. Das heißt, wir müssen jetzt hier einen Beginnblock davor packen und das Ganze mit umklammern. Wir können das jetzt hier einmal auskommentieren, damit wir das zu Referenz haben. Und um jetzt eine Funktion zur Kompellierzeit zu definieren, benutzen wir Defines-Syntax. Das ist ein neues Stück Syntax, das wir also definieren. Und das fängt wieder mit einer neuen offenen Kammer an. Und die Funktion zur Kompellierzeit wird aufgerufen mit einem Argument. Und das ist dieses Stück Syntax, was wir da jetzt angeben. So, um das jetzt zu übersetzen in diese Beginnform, müssen wir es ein bisschen auseinandernehmen, die Argumente identifizieren und dann parsen. Und um zu parsen, benutzen wir eine Bibliothek, die nennt sich Syntax-Pars. Das importieren wir jetzt als eine Bibliothek. Das ist nämlich kein normales Feature in der Standard-Bibliothek. Und wir werden dieses Syntax-Pars, werden wir benutzen innerhalb einer Funktion, die zur Kompellierzeit läuft. Oder zur Syntax-Kompillierzeit, wie wir gerne sagen. Deswegen benutzen wir das Keyword Force Syntax, damit wir das da benutzen können. So, das heißt, wir schreiben jetzt hier Syntax-Pars und wollen dieses Form benutzen. Und jetzt beschreiben wir ein Pattern, eine Form, ein Schema, nachdem ersetzt werden soll. So, wir fangen an mit Print und das hat ein Argument. Und dieses Argument ist eine Expression. Dazu benutzen wir diesen Doppelpunkt. Und das wollen wir jetzt übersetzen in ein anderes Stück Syntax. Um eine Syntax zu erstellen, benutzen wir eine Raute und dann ein Bactic. Und wir schreiben Display-Ark für das, was passieren soll. Jetzt benutzen wir das erstmal so und probieren das aus. Es ist erstmal nichts impront, aber wenn wir jetzt das Print Keyword benutzen, und dann funktioniert das. Aber es funktioniert erstmal noch nur mit einem einzelnen Argument. Wir wollen es ja mit mehreren benutzen. So, um das zu machen, naja, wie machen wir das? Wir wollen nicht nur ein Display-Ark, sondern wir wollen mehrere Argumente. Wie machen wir das? Wir tun hier drei Punkte hin. Es ist fast so einfach, um wahrzusehen. Und jetzt heißt es, dass wir beliebig viele Argumente, so viele wie wir wollen, lernen können. Und jetzt wie vorhin, müssen wir das in einen Beginn übersetzen, weil wir jetzt mehrere haben. Und wir wollen jetzt also so viele Displayaufrufe, wie wir Argumente haben. Und das können wir einfach machen, indem wir hier wieder drei Punkte hinmachen. Und Racket wird automatisch verstehen, dass das bedeutet, dass wir einfach einen Displayaufruf für jedes Argument haben sollen. So, jetzt können wir das Ganze nochmal ausprobieren. 1 plus 1 ist gleich und plus 1 ist eins. Und jetzt funktioniert das wunderbar. Nun, jetzt haben wir eine neue syntaktische Form zur Rackersprache hinzugefügt. Das ist relativ einfach. Und wir werden das jetzt als kleinen Syntaktblock für unseren kleinen Maschinenblock benutzen. So, das ist jetzt vielleicht ein guter Punkt, um unsere Arbeit abzuspeichern. Und deswegen nenne ich dieser Datei jetzt einfach Basic.Racket. So, okay. So, jetzt haben wir dieses Print hinzugefügt, was endlich dem Print in Basic ist. Aber das Basic-Programm sieht auch ein bisschen anders aus. So, wir haben jetzt hier Print Hello World zum Beispiel. Und dann hat man wieder nochmal eine Zeilennummer mit GoTo10. Es ist also immer das erste Programm, das das etwas wiederholt ausgeben kann. Naja, das ist natürlich noch nicht möglich. Und auch diese komischen Klammern sind auch noch im Weg. Wenn wir jetzt also eine neue Sprache in Racket stehen, wie man das macht, ist man startet mit dieser Zeilenbasierten Syntax, die wir aus der Apple-2-Syntax nachkennen. Also, auf eine Klammer. Wir sagen Hello World. Vielleicht müssen wir uns auch noch um den Print Case kümmern. Jetzt 20. GoTo10. So starten wir. Jetzt müssen wir überlegen, wie wir das übersetzen können mit dieses Racket. Das Programm macht noch nichts. Es feichert nur das Print-Command. Das heißt, es startet das Programm bei GoTo10. Es reicht nicht, dass in dem Print-Call, das wir besetzen, in order to store a piece of code, we'll just give it a name in Racket, right? So the idea is that we will translate it like this. Wir sagen, okay, wir definieren eine Funktion, nennen sie Line 10. Und wir nennen diese und sagen, sie soll Hello World callen. Und jetzt definieren wir noch eine Funktion, die wir Line 20 nennen. In Racket doesn't exist. Wir können einfach zu einer anderen Funktion gehen und nennen sie Callen. Und, well, you can already see that eventually this is hardly going to work at Line 10. Nicht funktionieren, weil Line 10 nicht bis Line 20 vorgehen wird. Und jetzt der Ruf in die richtige Direktion, in die richtige Direktion. We're just going to start with a single line and then we don't need to call that. Wir sagen jetzt wieder definiere Syntax. Und dieses Stück Syntax nennen wir Basics. Und wir starten mit einem einzelnen Kommando. Pass it the form and say, well, basic. Diese Basic Kommandos funktionieren. In der Racket Sprache heißt es expression. Well, we can see up there, right? Wir wollen es versetzen in Define. Wir sind ja auch eine Klamme. Wir werden jetzt etwas zu schieben bis später. Wir wollen hier einfach einfügen die Kommands in Körperdefizien. Okay, so that leaves the problem of computing the name. Das lässt das Problem aber übrig, dass die Namen der Technik sind. Well, I want to call a function called. Jetzt will ich eine Funktion rufen, die Format it heißt. Es nimmt etwas, was ich gleich erklären werde. Es ist ungefähr wie Funktion F, die Print F. Den Versuch hier wieder Leidnummer einzufügen. Ich muss einzustehen. Das Syntax Stück, das die Nummer repräsentiert, ist etwas anderes als die Nummer selbst. Wenn ich das mal eingebe, dann sage ich, es gibt ein Stück Syntax, das in die Nummer 10 enthält. Wenn wir die Nummer 10 wollen, dann kann ich eine Funktion rufen, die Syntax e heißt. Wenn ich diese Funktion nenne, dann gibt es mir die Nummer 10. Das ist das, was die Nummer ist. Ich muss hier die Syntax e nennen. Ich habe hier drei Punkte gelassen. Das nennt ein Argument, das der syntaktische Kontext genannt hat. Wenn es zu Identifikatoren kommt, dann, wenn es um Namen geht, kann ich mit den Namen zusammenstöße. Es geht hier nicht darum, dass es nicht der Hauptthema ist, dass es dieses Wort hat. Ein kleines Stück, wie das wir jetzt einfach mal konzentrieren müssen. Hier einfügen. Dieses Stück werden wir jetzt also hier einfügen, wir nennen das jetzt einfach Name. Und ich möchte das dann hier unten einfügen. Aber hier gibt es ein sehr subtiles kleines Problem. Und zwar, wenn wir hier eine Schema-Variabler haben, kann ich das einfach in der Syntax benutzen. Zum Beispiel kann ich da einfach dieses Kommand einfügen. Und Racket versteht, dass ich das da oben meine. Das, aber leider, also es gibt Gründe dafür, aber leider funktioniert das nicht bei so was wie die Name, was ein normales Binding ist und kein Patternbinding. Das heißt, um dieses Stück Code da zu evaluieren, muss ich Reautocomma eingeben, um klar zu machen, dass es sich hierbei um einen Ausdruck handelt, der, genau, dessen Wert wir haben wollen. Das ist vielleicht etwas verwirrend am Anfang, aber da gewinnt man sich dran. Also haben wir jetzt hier oben einen Pattern und haben wir ein normales Binding. Und wenn man das dann in der Syntax benutzen will, dann muss man da ein Reautocomma davor machen. Dass auch ein Unquote gelernt wird, also eine Entquotung. So, und jetzt sind wir schon in die ersten Fehler reingerannt, den ich gerade benannt habe. Dieses Syntax, die ich auf Leinnahme benutzt habe. Aber da geht das gleich. Ich kann nicht einfach so eine Patternvariable in normalem Code benutzen. Deswegen, was ich stattdessen machen muss, ist, dass ich da ein Prifix davor packen muss mit diesem Router-Backquote. Damit die Sprache weiß, dass sie in dem Namensraum der Patternvariable nachgucken muss. So, dieses Format minus id ist in einer separaten Bibliothek. Deswegen kennt die Sprache das aktuell noch nicht. Deswegen will ich nicht nur Syntax pass, sondern auch eine Bibliothek namens Racket-Syntax. Und jetzt kommen wir wieder zurück zu unserem Code. Wir führen das aus und wir haben keine Fehler. Es ist leise. Und jetzt können wir das einmal ausprobieren. Wir benutzen Basic, 10, Print, Basic 10 und dann Print Hello World. Passiert irgendwas? Es wurde jetzt einfach gespeichert, das Stück Code. Und wenn wir das jetzt aufrufen, dann wird das ausgeführt. So, Hurra, wir haben jetzt eine sehr einfache Version der Basic-Programmiersprache. Okay, so, jetzt haben wir ein einzelnes Kommando in einer Basic-Programmiersprache, aber natürlich wollen wir mehrere davon haben. Also, buchen wir jetzt mal, die wir das machen können. Dann jetzt Syntax pass, dass da mehrere Zeilen drin sein können mit diesem Punkt, Punkt, Punkt. Das müssen wir natürlich dann vielleicht anders übersetzen. Aktuell hat unsere Expansion nur eine einzelne Definition für eine einzelne Zeile. Aber das müssen wir jetzt erweitern, damit das auch mehrere Definitionen unterstützt. So, um das zu machen, also Racket ist eine funktionale Programmiersprache, also benutzen wir eine Map. Und das heißt, jedes Stück Syntax wird also eine bestimmte Definition jetzt zurückgeben. Wir benutzen ein Lambda, da machen wir wieder drei Punkte rein. Das ist ein subtiler Aspekt, zu dem ich vielleicht zurückkommen werde. So, und worüber wollen wir jetzt unsere Map anwenden? Nein, wir wollen über alle und Zeile alle Kommandos anwenden. Das heißt, wir benutzen jetzt einmal hier die Zeillennummer. So, wir haben aber nicht nur eine Zeillennummer, sondern mehrere. Also brauchen wir wieder die Punkt, Punkt, Punkt. Also müssen wir es so benutzen, mit diesen drei Punkten dahinter, sonst wird Racket nicht verstehen, was wir meinen. Und diese Zeillennummer ist jetzt also eine Patternvariable. Und diese Patternvariablen funktionieren nur mit diesem, mit dieser rote Backquote-System. Und jetzt brauchen wir das gleiche nochmal mit den Kommandos. So, wir sind aber immer noch nicht fertig, weil, wie ihr euch vielleicht erinnert, dieses rote Backquote gibt immer ein Syntax-Objekt zurück, aber wir wollen es ja in Lisp, also, weil wir müssen es ja in Map eingeben. So, Map hat eine nützliche Funktion, die wir da benutzen können, die sich Syntax to List nennt. Und das ist auch für die Kommandos, brauchen wir das auch. Und das erlaubt uns, über diese Syntax-Elemente zu viterieren. So, und jetzt kommt hier Subtilität. Da, wo wir jetzt plötzlich hier Lein-Nummer-Kommand als normale Lambda-Parameter genommen haben, sind es nicht mehr Patternvariablen. So, das ist jetzt der subtilste Aspekt von diesem ganzen Makrosystem, was wir in Racket haben. Wenn man das hier genau versteht, die Patternvariablen kommen von da oben, und alles andere, alles, was nicht da oben in den Syntax-Pars Patternvariablen ist, ist keine Patternvariablen. Das heißt, in diesem Fall sind es normale Variablen, und deswegen werden sie nicht benutzt und brauchen auch keinen Hashback-Wout mehr. So, und das bedeutet auch, dass Racket jetzt nicht einfach so die Patternvariable durch ihre Expansion ersetzen wird. Deswegen müssen wir hier nochmal so einen Rautocommadervor machen. Wenn ihr das verstanden habt, dann wird es euch schon ganz gut gehen dabei. So, wir haben keine Fehlermeldung. Wenn wir das Ganze ausführen, wir probieren es noch mal. Dieses Basic-Programm und oh, es beschwert sich. Da ist wieder eine Trap, bzw. es ist nicht eine Trap, sondern etwas, was wir schon mal gesehen haben. Und zwar im Map gibt es eine Liste zurück. Das sehen wir hier mit diesem Anführungsstrich und der Klammer auf. Und das bedeutet, dass Map eine Liste produziert hat, aber Racket möchte eigentlich ein Syntax-Objekt. Wenn man jetzt aus Lisp oder Clojax kommt, dann erwartet man eigentlich, dass eine Liste auch als Syntax-Objekt nutzbar ist, aber Racket trennt das sehr explizit. Und das erlaubt uns, die bessere Fehlermeldungen zu bekommen und auch um die Koti-Gene für die lexischen Bindungen innerhalb der Sprache. Das ist nicht wichtig für diesen Talk, aber ist, just so you know, damit ihr es wisst. So, wir haben jetzt mehrere Definitionen hier also. Und da wir mehrere Definitionen in eine syntaktische Form bringen wollen, benutzen wir wieder dieses Beginn. Und genauso wollen wir das Ergebnis dieser Map, die Ergebnisse der Evaluierung. Also brauchen wir hier immer das Rautekommer. Und jetzt haben wir leider noch ein kleines Problem, noch eine kleine Subtivität, wenn wir diese Expansion jetzt angucken. Also hoffentlich seht ihr das, wenn wir das jetzt expandieren, dann würde das expandieren in sowas wie Beginn. Und dann geht wieder eine Klammer auf. Und dann innerhalb der Klammer auf geht ist zum Beispiel die Definition der verschiedenen Zeilen. Also zum Beispiel Zeile 10, Zeile 20 und so weiter. Und wenn man das sich jetzt genau anguckt, dann sieht man, da ist genau ein paar Klammern zu vier. Wir wollen die Markoexpansion nicht da auch nochmal eine Extra-Liste hinzuzufügen, sondern wir wollen nur die einzelnen Elemente der Liste direkt hintereinander. Und deswegen müssen wir hier nochmal einen kleinen magischen Charakter hinzufügen, der genau das für uns macht. Erlaubt uns diese Art von Slicing und macht genau was wir wollen. So, das ist wie das Rautekommer, aber das erwartet explizit eine Liste und gibt sie als einzelne Elemente zurück und nicht als eine Liste. So, jetzt haben wir hier diese Basisform. Und wenn wir die jetzt ausführen, dann sehen wir, es gibt Hello World aus und das funktioniert einfach so. Und jetzt können wir zum Beispiel hier auch eine zweite Zeile hinzufügen. Zum Beispiel aktuell wissen wir EnoPrint, also könnten wir jetzt hier hinzufügen, hier dieses zum Beispiel hinzufügen. Und so, wenn wir jetzt nochmal Linen 10 aufrufen, dann sehen wir, Zeile 10 wird ausgeführt, aber eigentlich würden wir erwarten, dass das Programm auch zu Zeile 20 direkt danach geht. Aber wir haben noch nicht gemacht, dass die Zeilen miteinander verbunden werden und das wird also unser nächstes Problem. Also, was wir nach dem Kommando jetzt machen müssen, ist, dass wir die nächste Zeile aufrufen müssen. So, wie machen wir das? Um die nächste Zeile zu produzieren, brauchen wir die Zeilennummer der nächsten Zeile, aber die wissen wir nicht. Wir wissen nur die aktuelle Zeilennummer. Und wie wir das machen, ist, dass wir jetzt über eine weitere Liste iterieren. Und zwar nehmen wir einfach die ursprüngliche Zeilennummer Liste, aber wir verschieben sie um eins. Also, so dass wir dazu benutzen wir die Listenfunktion Kudo, die das erste Element der Liste entfernt und das gibt uns das. Und jetzt wird natürlich Maps nicht beschweren, weil die Listen nicht länger, die dieselbe länger haben. Es muss nur dieses Element appenden, weil da keine Zeile mehr nach der letzten Zeile ist, muss ich noch einen in Klammern Falls einfügen. Okay. So, ja, das ist gut. Ah, wir brauchen natürlich, dass wir jetzt über drei Listen, nicht über eine, mapen. Jetzt kann man sagen CoreNextLine. Und jetzt kann ich noch einen Code, den Format ist. Und das wäre so eine Reinaustraktion. Wir können es natürlich noch extrahieren zu den eigenen Funktionen. So, copy it one last time. Stick it up here. Dann halten. Jetzt müssen wir beachten, um diese Funktion zu berufen. MakeLineName. Jetzt müssen wir natürlich die LineNumber einfügen. Also, dieses Basic, das sage ich gerade an der Stelle, referenziert natürlich auch das Basic. Wir müssen natürlich auch als Argument einfügen. Und dieses Kontext, der Kontext so viel macht, stellt sich hier, dass die Argumente in dem richtigen Mainfall besuchen. Dann können wir die Funktion, also das Basic, das sage ich gerade an der Stelle, in dem richtigen Mainfall besuchen. Dann können wir die beiden Calls und Schärmen. Und jetzt können wir rufen, CoreNextLineName. Das Basic-Sing können wir dann, aber das Basic-Tier können wir dann in dem CoreNextLineNumber zeigen. Das machen wir dann nochmal hier. Das heißt natürlich MakeLineName. Wir können hier auch noch rufen, das hier noch mal replizieren. Natürlich, jetzt kommen wir zu dem Fakt, dass es kein Call ist in einem Moment. Diese Definition, MakeLineName, ist nur eine reguläre Prozedur. Es wird nicht der Fickr sein, bis Runtime passiert. In order to tell Racket, no, this is a function, please make it available to MacroExpansion. Okay, bitte, wir müssen definieren, das ist für uns ein Docksportfickr. Okay, eine andere Sache. Das ist natürlich kein Call, das ist ja, um die Funktion, das ist natürlich kein Call, um ein Call zu haben, müssen wir den Klammern haben, um die Namen der nächsten Zeile. Also mache ich das jetzt, ich mache die Klammern so, und dann habe ich das mit der Routin, mit der Routin, mit der Routin davor. Jetzt bin ich ganz ready, denk da, NextLineNumber kann auch fort sein. Wir haben hier keine Funktionen, die wir aufrufen können. Wir müssen also hier eine Fallenterscheidung machen, und wenn es eine NextLineNumber gibt, dann, das ist eine gute Funktion, die wir aufrufen können, das ist die nächste, und andere Plätze. Woid, also eine Funktion, die absolut nichts macht, die ist eingebaut in Racket. Okay, lass uns das probieren. Okay, ja, NextLineNumber, Referenz ist nicht, ich konnte nicht identifizieren. Lass uns das noch mal probieren. Jetzt geht es durch. Lass uns das sehen. Wir haben dieses Programm mit zwei Zeilen drin. Wir rufen den Zeil 10 auf. Ja, jetzt ruft es beide Seiten auf. Es läuft. Wir haben einen kleinen Pedal gemacht bei der Implementierung von Print. Wenn man Print aufruft, dann tut es auch an den Zeilennummern am Ende aus. Da zieht man Ruh und macht dann noch eine New Line-Netzung. Und das sollte wir funktionieren. Wir machen das nochmal. Und hier sehen wir den Out. Ausgabe so, wie wir sie wollten von erfahren. Jetzt haben wir ein einfaches Programm mit mehreren Zeilen. Und jede Zeile kommt nach Hanana. Was passiert als nächstes? Die berühmte GoTo-Funktion, wo man einfach irgendwo hinspringt, ohne dass man irgendwie weiß, was davor passiert ist. Wenn wir also unseren Code anschauen, dann sehen wir, dass GoTo eine besondere Behandlung braucht. Wir implementieren nicht nur einfach Print, weil die Sache so ist, dass wenn man ein GoTo implementiert, dann sollte da kein Code in der X-Line sein. Aber das natürlich zusammen. Jetzt wird es ein bisschen, ein bisschen in dieser langen Funktion. Deshalb werde ich die Code, das in eine eigene Funktion, implementieren. Wir sehen, wenn wir das funktionieren. Die Feindvolle Syntax, eine Funktion, die von im Translate-Kommand wir geben dahinein den Code-Mando, den Befehl, den originalen Befehl. Und die Befehl, dass wir den Rekord übersetzen. Die Idee ist, dass wir den Körper dieser Funktion produzieren. Also müssen wir auch GoNex-Line da rein übergeben. So, das machen wir hier. Cool. Jetzt halten wir das alles ein. Wir behalten die Definition von dort oben. Erinnert euch, wir können nur eine Form pro Zeit verhandeln da haben. Wir können also zwei Formen in eine zusammenpacken, die wir vorher beginnen zu bleiben. Jetzt wollen wir hier keine neue Funktionität, sondern wir übersetzen einfach. Deswegen Translate-Kommand und dann Kommand und das. Schauen wir mal, ob das funktioniert. Das Programm läuft so wie vorher. Wir wollen also GoTo besondere Behandlungen geben. Um das zu tun, wollen wir den das Kommando angucken und schauen, wenn das ein GoTo-Kommando ist, dann müssen wir das Besonderes machen. Ansonsten gehen wir einfach zu dem Kurt, den wir schon geschrieben haben. Also, Zünderspars-Kommand. Und jetzt mache ich hier ein Pudder, eine Schablone. Wenn das GoTo ist, dann leihen wir da ist eine Zellnummer. Und wenn wir in anderen Fällen den Teil den wir schon kennen. Also, jetzt gibt es hier einige Probleme, dass es bei den zweiten und jetzigen einer passt. Und der der passt, der wird ausgesagt, dass hier ist ein Wildcard-Pattern, das einfach alles matcht, was man so findet. Okay, in diesem Fall, wenn es ein GoTo ist, dann gibt es eine besondere Besonderheit. Wie Bassino-Pattern-Matching, also wenn ein Name GoTo ist, dann haben wir den Bub. Also, wenn wir wollen, dass das Wort GoTo hier steht und nicht mehr in unserem Namen. Also, dann kann das Programm zusammengekommen werden. Um das zu sichzustellen, müssen wir ein Matche-Tool aus den Schreiben. Und wenn das Wort GoTo irgendwo hier auftaucht, dann wollen wir wirklich das Wort GoTo dort haben. Also, alle besonderen behandelten Konten der Fälle müssen wir diese Befehlkommando hier genau würdlich nehmen. Wenn da also ein GoTo ist, dann wollen wir einen Aufruf generieren. Auch die Prozedur, die diese Zeilnummer betrifft. Und diesen Namen bekommen wir durch MakeLineName. Jetzt ist es das Kontext. Argument, das haben wir hier nicht. Wir müssen also einen neuen Parameter hinzufügen, nämlich Kontext. Und zwar als Basing. Okay. Und das haben wir hier. Und dann können wir MakeLineName, Kontext und LineNumber aufrufen. Okay. Und das ist aber nur der Name der Prozedur. Wir müssen also noch ein Detz machen, was so passiert. So, das ist Warte. Warte, komm mal. Den Namen da können wir mal schauen, ob es irgendwas macht. Oh, da ist ein Fehler-Meldung. Ja, ich bin wieder in die selbe Fälle gelaufen. LineNumber ist eine Pattern-Variable. Es ist also nur innerhalb der Hashtag-Gelte. Also Warte in der Anfangstechnik. Wir benutzen es aber eine normale Variable hier. Und deswegen müssen wir wieder Spackbackquote, also die Rote Anfangstechnik davor schreiben. Das wirkt vielleicht verwirrend, aber wenn du es ein bisschen mit dem Akkus verarbeitet hast, dann wirst du das Gefühl dafür bekommen. Und die Fehler-Meldungen erkennen. Hier, ich mag das ja auch. Die Pattern-Variable kann nicht außerhalb eines Templates benutzen. Genau. Aber das sieht hier aus, zu sehen. Mal schauen, ob das klappt. Hier ist eine Regel, die ich vergessen habe. Aber die Fehler-Meldung, Erinnerung, literally ist unbound in Phase 0. Phase 0 ist die Laufzeit. Ich mach mir jetzt nicht so viel Sorgen. Aber das bedeutet, BOTO hat keine Definition. Wir sind aber nicht in der Definition so interessiert, weil wir wollen das nur übersetzen in irgendetwas anderes. Deswegen machen wir eine Dummy-Definition, die einfach fortsetzt. Da gibt es eine Reference zu diesem Gotem. Wenn es dann einen gibt, dann wird unser Syntax-Parser das erkennen. Mal schauen. Wir versuchen mal das Programm laufen zu lassen. Das ist schön. Ich glaube, es ist Zeit, dass ich hier ein richtiges Basic-Programm einfüge in den Source Code, damit ich es nicht immer wieder neu tippen muss. Wir schreiben das mal so. Um zu sehen, dass es funktioniert, werden wir jetzt mal hier eine Zeile einfügen, die heißt Go 40, und das ist jetzt nicht 30. Und dann machen wir eine neue Zeile 40, und das Ende. So, jetzt ist das Programm fertig. Mal gucken, ob das ausprobiert. Wir rufen das jetzt auf und sehen tatsächlich, Hallo Welt, das Ende ist überspringt, also Zeile 30. Go 2 funktioniert jetzt. Was als nächstes, wir können jetzt einfach neue Kommandos hinzufügen. Aber natürlich, manche sind ein bisschen problematischer als andere. Eins, das zum Beispiel ein bisschen problematisch sein könnte, also, wir könnten jetzt hier zum Beispiel A ist gleich 42 haben. Das würde eine Variable erstellen, die A heißt, und die könnten wir dann nachfolgend benutzen. Das ist dann ein bisschen anders vielleicht, muss die Operate davor sein. Und außerdem müssen wir dieses Doppelpunkt ist gleich benutzen, um die Verwirrung mit dem Gleichheitszeichen ein Doppelpunkt zu vermeiden, was der Gleichheitsoperator ist. So, wir werden jetzt also die gleiche Technik benutzen, wie wir vorher schon benutzt haben. Wir werden also hier zum Beispiel beim literal Go 2 sagen, dass wir das nur benutzen wollen, wenn da auch wirklich nur ein Doppelpunkt ist gleiches ist. Und das muss also ein Identifikator sein. Also es muss ganz klar sein, dass dein Identifikator ist auf der anderen Seite kann dann irgendein Ausdruck sein, den wir haben wollen. So, haben wir das jetzt also. Jetzt müssen wir nur ein Stück Code generieren, was damit funktioniert. Und ein Racket kann noch mal Zuweisung mit dem Operator SetAusrufungszeichen Das Ausrufungszeichen wird anscheinend als Bang ausgesprochen. Und das heißt, wir benutzen jetzt diese beiden Doppelbuster Variablen. Und SetAusrufungszeichen ist jetzt wieder etwas schiefgegangen. Den gleichen Tick mit dem Go 2 müssen wir jetzt hier oben auch nochmal für das Doppelpunkt ist gleich benutzen. Dummy Definition. Und jetzt haben wir hier noch ein kleines Problem. Da heißt es, dass A ein ungewundener Identifikator ist. Und woran liegt das? Naja, Racket unterscheidet zwischen der Bindung eines Identifikators. Das ist klar zu machen, dass es existiert. Und der Erstellung danach. Das sind zwei unterschiedliche Dinge. Das heißt, wir könnten diese Fehlermeldung wegmachen, indem wir vorher den Wert A zu irgendeinem Dummywert definieren. Aber das ist natürlich jetzt nicht verbasic, weil es ... So, wenn wir da jetzt diese Definition brauchen, es wäre nicht verbasic. In Basic könnten wir einfach diesen Identifikator erstellen, indem wir ihn zuweisen. Wir können irgendwie vorher ein Set an Variablen generieren, damit wir wissen, was für Definitionen wir brauchen und die wir dann benutzen können. So, um diese Definitionen zu generieren und wie wir das machen, brauchen wir im Proje wie ein Set an Variablen, die ... und dass das Programm wird halt beginnen, indem es alle Variablen sammelt, die in einem Kommand übernutzt werden. Dazu definieren wir erstmal eine Funktion hier. Mal Variablen sammeln, Collect Variables und übergeben hier ein Kommando. Und nicht nur ein Kommando, sondern auch wollen wir quasi eine Liste bzw. eher ein Set an Variablen, weil eine Variable kann mehrmals erscheinen. Das heißt, wir werden z.B. so was wie ein ItSet ein Identifikatoren Set benutzen. Das werden wir gleich definieren. Und Racket hat eine Bibliothek, die uns erlaubt, diese Bibliothek an die Identifikatoren zu erstellen. Diese Bibliothek können wir jetzt einmal hier oben einfügen. Die nennt sich Syntax-Iset und die erlaubt uns mit diesen Set umzugehen. Und jetzt werden wir halt in diese Kommandos reingucken müssen. Deswegen benutzen wir wieder die gleiche Syntax-Pars, die wir auch schon da oben benutzt haben. Wir haben verschiedene Literale, die wir benutzen. Mit GoTo ist da z.B. keine Variable drin. Das interessiert uns nicht. In diesem Fall geht es uns insbesondere um die Zuweisung. Also das Topfpunkt ist gleich. Wie funktioniert das jetzt? Also in diesem Fall, was wir machen müssen, ist, dass wir diese Funktion FreeItSetAdd benutzen müssen zusammen mit dem Set und einer Variable. Und alles andere, naja, da machen wir einfach gar nichts. Da rufen wir einfach diese Void-Funktion, die leere Funktion auf und machen gar nichts. So, okay. Und jetzt müssen wir diese Variablen sammeln Funktionen aufrufen vor jedem Kommando. Das machen wir jetzt hier oben direkt in unserer Basic-Basis-Definition. Und das heißt, wir rufen hier dieses Set und rufen jetzt eine Funktion auf, die einen nervigen Namen hat, die nennt sich MutableItSet, also das bearbeitbare, freie Identificatoren-Set. Und da gibt es eine Unterscheidung, ob Identificatoren frei oder gebunden sind. Aber das ist für uns erstmal nicht so wichtig, aber dieses hier wird funktionieren. Und was wir jetzt also machen wollen, ist, dass wir diese Collect Variables, die Sammelfunktionen aufrufen wollen und übergeben ihr dieses ItSet, was wir erstellt haben. Und wir haben aber natürlich nicht nur eine Variable, sondern wir haben mehrere Variablen und Kommandos. Das heißt, wir rufen jetzt hier nochmal Kommand mit Punkt, Punkt, Punkt auf. Wir verwandeln das wieder in Analysten wie vorher. Und jetzt können wir eine eingebaute Funktion namens diese Funktion einfach auf jedes Argument, auf jedes Kommando in dieser Liste aufruft. So, genau. Und das nimmt alle Variablenzuweisungen und erstellt aus die Variablen und ignoriert alles andere. So, jetzt haben wir also einen Satz an Variablen-Definitionen. Jetzt müssen wir aber erstmal diese Definitionen erstellen. Das machen wir, bevor wir den tatsächlichen Code des Basis-Programms aufrufen und die wir benutzen, um über alle Variablen zu iterieren. Und was wir da machen würden, ist, dass wir gerne über dieses Itset iterieren würden, aber Itset ist keine Liste, deswegen können wir das grundsätzlich nicht. Deswegen benutzen wir eine FreeItSetToList-Funktion, die das Ganze in eine Liste verwandelt. Und um jetzt diese Dummy-Definition zu machen, brauchen wir wieder hier einen Sintagsblock, den wir erstellen standardmäßig setzen wird, in Wert der Variable auf Falsch. So, jetzt müssen wir uns wieder erinnern, dass das bei dem anderen Map-Aufruf, wo so das auf verwandelt werden, in eine leere Sequenz ohne Klammern, und das müssen wir jetzt hier oben auch genauso nochmal machen, diese komischen Magie, mit dieser Raute.Add, dass diese Definitionen in die Sequenz an Kommandos aufdröselt. So, jetzt sagt uns der Code, ah, diese Variable ist schon definiert, man kann nicht mehrere Definitionen haben, das ist ein gutes Zeichen. Und jetzt löschen wir das, und kompilieren es normal, und das sieht gut aus. Und jetzt sehen wir, dass da eine Variable ist, die erst mal als Falsch definiert ist, bzw. gebunden ist. Und jetzt probieren wir mal das Programm auszurufen. Wir fangen mit Zeile 5 an. Woran liegt das denn? Das weiß ich jetzt nicht. Woran könnte das liegen? Ich muss jetzt mal ansehen, ah, ah, hat jetzt den Wert 42, wir wissen nicht genau, warum, aber so, das heißt die Übersetzung, ah, und da sehen wir jetzt, wir haben was vergessen. Das setzt nur die Variable, aber es macht nicht weiter mit dem Programm. Wie der Rest der verschiedenen Werte macht das. Deswegen müssen wir hier nochmal so ein Beginn hinsetzen, und dann diese CallNextLine-Methode und jetzt gibt das tatsächlich den Wert der Variable aus. So, jetzt haben wir Variable in zur Werte in unserem Programm. Als nächstes Common Conditionals, sogenannte, also konditionelle Ausführungen. Darunter ist vor allem das IF, das Wenn-Statement. Da ist zum Beispiel, kann man hier vergleichen, wenn eine Variable einem Wert entspricht, dann kann man eine Sache machen oder an denfalls kann man eine Variable auf 2 setzen. Und jetzt geben wir hier unten nochmal B aus, um zu gucken, was erstellt wurde. So, jetzt müssen wir dieses IF-Kommando, dieses Wenn-Kommando natürlich wieder übersetzen und gehen deswegen wieder in unsere Kommando Übersetzungsfunktion und erstellen ein weiteres Pattern, um das IF-Kommando zu erkennen und haben da eine Test-Expression, also ein Test der durchgeführt werden muss für die Wenn-Expression, und dann die beiden Code-Blocke, die eventuell ausgeführt werden. So, der Wenn-Zweig und der Anhaltszweig die müssen auch beide ausgeführt werden. So, dieses IF jetzt hier, das ist jetzt ein Racket IF und nicht eins aus unserer Basicsprache und wenn der Test wahr ist, dann wollen wir das Ergebnis der Kommando-Übersetzung, das heißt, wir tun da noch mal einen Rautekommando davor und benutzen den Kontext, den aktuellen Kontext bis Kommando und machen hier noch mal einen Rautobaktik dahinter für dieses Wenn und dann im anderen Fall benutzen wir noch das Gleiche und da benutzen wir auch wieder das Racket-Bells und jetzt geben wir das aus und haben erstmal einen Fehler, weil B ungewohnt ist. Naja, woran liegt das? Naja, wir erstellen Definitionen für Variablen, aber bisher gucken wir uns nur an Zuweisungen an, die am Anfang passieren, auf der ersten Ebene passieren. Das heißt, wir müssen jetzt Variablen schon am Anfang erstellen, auch wenn sie in Untercode-Blocke sind, nicht nur, wenn sie auf der ersten Ebene sind. Das heißt, wir müssen das hier jetzt einmal runterkopieren und was wir jetzt also machen müssen ist, dieses Variablesammeln-Funktionen nochmal rekursiv auf die Unter- und Zweige aufzurufen, zum Beispiel hier mit einmal dem Send-Zweig, dem Dann-Zweig und einmal mit dem Else, also dem anderen Falls-Zweig und die jeweils auch dem It-Zweig hinzufügen. So, wir probieren das nochmal, und wir zeigen jetzt nochmal unser Programm, und wenn wir es jetzt aufrufen mit Zeile 5, dann kriegen wir das, das sieht schon mal nicht schlecht aus, da sehen wir, da ist die Nummer 1, das kommt hier aus dieser Zuweisung und B wird ja auf 1 gesetzt, wenn A 42 ist und A ist tatsächlich 42. Wenn wir jetzt auf 41 beispielsweise und das nochmal ausführen und Zeile 5 ausführen, dann sehen wir, dass es jetzt eine 2 ausgibt, und das ist der anderen Falls-Zweig, der aufgerufen wurde. So, lass uns das mal kurz zurückändern und abspeichern. So, wir werden noch eine Funktion hinzufügen, die ist nicht nur ein Kommando, sondern 3 Kommandos und die nennt sich ungelegende Subroutinen. Wie könnte das aussehen? Zum Beispiel, wenn wir jetzt hier eine Zeile 5.000 haben, könnte man nochmal GB Variable ausgeben und dann in Zeile 1010 ein Return Statement an, um zurückzugehen. Basic hat grundsätzlich keine Funktionen wie wir sie kennen, sondern Methoden haben einfach nur diese Subroutinen. Das heißt, wir können die Ausgabe von B einfach abrufen, indem wir hier zum Beispiel in Zeile 8 hier runter 1.000 und die Idee ist, dass der dann zu Zeile 1000 springt, zu diesem und dann zurück zur ursprünglichen Stelle kommt mit diesem Hello World und dass das zu Ende ist. Und das kann man natürlich auch mehrmals aufrufen. Okay, so. Dann werden wir das jetzt mal versuchen. Das sind 2 Kommandos. Einmal Go Sub und einmal Return. Also, gehe in eine Subroutine und kommen wieder zurück. So, müssen wir jetzt natürlich etwas machen mit der Kommando-Übersetzungsfunktion. Und machen erstmal noch ein paar Dummy-Funktionen, um die Federmeldungen zu vermeiden. Und sehen jetzt hier, das Go Sub ist ja eigentlich dem Go 2 sehr ähnlich. Wir müssen auch einfach zu einer anderen Zeile springen. Das heißt, wir können das hier dazu fügen und wir müssen jetzt also einen Aufruf-Übmach erstellen, wie mit Go 2. Das heißt, wir duplicieren das einfach mal genauso wie es da oben ist. Der Hauptunterschied ist, dass wir nachdem der Aufruf fertig ist, nachdem der abgeschlossen ist, dann danach müssen wir weiter mit der nächsten Zeile machen. Zum Glück wissen wir, dass wir genau wissen, wo diese nächste Zeile ist. Die ist da oben. Das heißt, wir rufen jetzt einfach mal hier dieses Call nächste Zeile aufrufen an. Und außerdem brauchen wir jetzt noch die Definition für Return. Und wenn diese Return, also die zurückgehenden Funktion aufgerufen wird, die geht nicht zur nächsten Zeile, sondern sie macht tatsächlich einfach gar nichts. Sie wird einfach zurück zu der Zeile, nach dem ursprünglichen Gehe in eine Subroutine auf. So, wir probieren mal schnell aus, ob das funktioniert. Und sehen, da kam jetzt ein paar Zahlen raus. Ich weiß jetzt nicht mehr, was das Programm genau gemacht hat. Da wurde eine Subroutine aufgerufen, wo B ausgegeben wurde und dann wieder zurückgekommen wurde. Und eigentlich sollte das Programm jetzt hier beenden bei The End, aber es hat da unten doch noch den Print ausgeführt. Aber das ist jetzt eine Aufgabe für euch, die Zuschauer immer zu gucken, wie ihr das machen würdet. So, jetzt haben wir eine grundsätzliche Idee von, wie das Ganze funktioniert. Und ihr wisst wahrscheinlich, wie ihr den Rest machen könntet. Und ich habe euch auch gezeigt, wie man eine ungefähre Basics-Interaction kriegt. Aber wir haben noch ein paar Minuten, das zeige ich euch jetzt. Wir müssen jetzt noch was einer Provide, eine eine Begebungsform Wir müssen jetzt noch die Basicsform ergeben. Wir müssten jetzt noch so an ein So, für die wirkliche Basics-Intax es gibt keinen Weg um den Partner herum. Wir haben nicht genug Zeit um das zu tun. Also sie ist ein Datei, die Basic-Lieder genannt wird. Also Basic-Lieder. Es gibt eine Funktion, die Paras line genannt wird. Das andere Argument ist eine Referenz auf ein offener Detail. Wir sagen jetzt einfach open, öffnen in input string und gib aus A plus 1 und wir sehen, es gibt zurück das was hier oben eingefüttert gibt es zurück. Wir müssen seine geune heizer-platten Funktionen schreiben. Diese Funktion heißt Paras Broker ist das gleiche wie Paras Line, aber für ein gesamtes Programm machen wir das. Jetzt wollen wir noch das Ergebnis einfügen. Jetzt müssen wir noch eine Module-Deklaration erschaffen für Basic. Es importiert die Racketssprache. Es importiert den Code, den wir jetzt gerade geschrieben haben. Es ist zwar in den Basic-RKT das Ergebnis ein. Es ist eine ganz normale Syntax-Objekt. Jetzt nehmen wir eine Funktion, die heißt Datum nach Syntax. Jetzt müssen wir noch diese Funktion exportieren. Wir nehmen ein leicht unterschiedlicher Name. Wir nehmen es einfach Syntax, also Syntax-Lesen. Jetzt speichere wir die Racketssprache. Jetzt habe ich noch eine Racketssprache, die heißt Basic-RKT. Wir haben jetzt so ein Basisleser hier, mit dem wir dieses Beispiel-Programm jetzt einmal laden können. Wenn wir das jetzt einmal ausführen mit der Zeit 10 Funktion dann sehen wir, dass es mehr oder weniger funktioniert. AppStoft ist nur ein ganz kleines bisschen anders als normales Racket. Man kann wirklich jede Sprache, die man will, mit dem Racketsystem implementieren. Es ist nicht nur für Spielzeuge sondern man kann damit auch verschiedene Software-Architekturen und die Notation für Dokumentationen. Das kann man auch im Racketsystem machen. Es gibt sehr viele Dokumentationen und Beispiele und Tutorials. Man kann natürlich die gleiche Methode auch benutzen, um Sprachen, die für die Lehre gedacht sind, zu erstellen. Ich hoffe, das hat euch jetzt motiviert, mal das Racketsystem auszuprobieren. Wieder probiert, guckt euch gerne das Github-Repository an, wo all der Code ist und was ihr hier interessiert seid an diesem Zeug. Ich bin sehr gespannt auf einige Fragen und Feedback. Danke vielmals Danke, Mike, für den Talk und dafür uns einzuführen zu Racket und das mal zu zeigen und dass du uns gezeigt hast, wie man eine Sprache dort implementiert. Ich habe schon gesehen, dass im Chat schon einiges an Diskussion war, aber wir haben noch ein paar Fragen für alle und wir fangen an mit einer Frage von irgendjemandem und die Frage ist, warum benutzt man diese Makros anstatt einfacher Funktionen weil die Person denkt, man kann auch einfach Funktionen benutzen um Prinz zu produzieren. Ja, diese Frage wurde gefragt, direkt nachdem ich Prinz gezeigt habe, es referenziert nicht den Rest und ja, das stimmt. Prinz könnte auch einfach eine Funktion sein, aber es gibt viele Dinge, die tatsächlich einfach Funktionen sein könnten, die nicht Makros sein müssten in Racket und Prinz ist definitiv eine davon, wie sie benutzen können, um euch zu zeigen, wie ein simples Makro funktioniert. Die restlichen Makros, die ich gezeigt habe, sind erheblich kompliziert, deswegen dachte ich mir, ich mache mir erstmal ein einfaches. Sehr gut. Dann lass uns weitergehen. Da sind die ganzen Unterschiede zwischen Bindings und Operatoren, Bindungen und Operatoren. Es wäre super, wenn du zeigst, was ... Ja, okay. Es ist ein bisschen verwirrend, dass es unterschiedliche Arten von Variablen gibt und ich hoffe durch den Talk gab es da genug unterschiedliche, dass man sehen kann, was der Unterschied ist. Der Grund dafür ist, dass es hier ein fortgeschrittenes Makrosystem ist und das einfache Makrosystem in Racket, und die Konstruktion dafür ist ganz simpel und kennt nur die Pattern, die die Mustervariablen sind. Wenn man damit arbeitet, aber wenn man komplizitere Makros schreiben will, wie unsere, dann wird man natürlich sowohl Patternvariablen als auch normale Variablen kennenlernen müssen. Aber es gibt auch wirklich nur diese beiden und man versteht das relativ schnell, wenn man damit eine Weile arbeitet. Okay, dann gibt es einen weiteren Kommentar. Es gibt all das Wechseln zwischen Raute Backquote und Raute erinnert die Person an den Mathe-Modus in Latex. Ja, das ist eigentlich ein guter Vergleich in Latex. Wenn man ein Dollar-Symbol einfügt, dann gibt man einen anderen Syntax-Block in eine ganz andere Welt und dann kann man da wieder rauskommen, als Kommando benutzt. Das ist genauso ein sehr ähnliches Konzept, eine ähnliche Idee wie ein Racket, dass man in den Syntax-Konstruktion-Modus geht und dann wieder rausgeht in normalen Code und dann kann man ihn wieder reingehen, dass man das halt so rauspoppen und rein pushen kann, die unendlichen Schildkröten. Die nächste Frage ist, ob man die Kontrolle über die Laufzeit in der Sprache hat in Racket? Ja, ich sehe auch, die Frage ist ein bisschen länger und geht auch auf Memory Management ein. Also Racket gibt einem tatsächlich sehr viel Kontrolle über die Laufzeit. Es hat eine extrem flexible Laufzeit, vielleicht über die flexibelste Laufzeit, die es überhaupt gibt für eine Programmiersprache. Insbesondere wenn es um Kontrollstrukturen geht. Makos sind ein Aspekt davon. Aber das andere ist auch, dass es eine delimitierte Kontrollstruktur wird. Das heißt, man kann Exceptions und Threading innerhalb der primitiven Konstruktion innerhalb der Sprache benutzen kann. Es gibt einem nicht die Kontrolle über Speichermenagement. Man kann ein bisschen beeinflussen, wie Racket mit Speicher umgeht, aber es gibt einem nicht die Kontrolle darüber, ob es interpretiert oder kompiliert ist. Es gibt eine Menge Beispiele, die mit der Racketsprache kommen und dem Paket aus ganz vielen verschiedenen Sprachen. Es gibt zum Beispiel eine hassleartige Sprache, wo der einzige Unterschied ist, dass es eine Klammerung hat und das Racket-Basis-System hat auch eine Sprache, die funktionale, reaktive Programmierung benutzt, wo man halt live alle Variablen sich ändern sehen kann, während das Programm ausgeführt wird. Die nächste Frage ist von Dakota. Hast du einen kompletten Basic-Interpreter in Racket geschrieben und wie ist im Performance-Tag gleich mit einem traditionellen Interpreter? Naja, ich habe ein bisschen mehr interpretiert, als ich gezeigt habe, aber ich habe nicht alle Funktionen von Applesoft inklusive Grafik implementiert. Aber wie man sieht ist, da es in Macros implementiert ist, ist es im Endeffekt ein Compiler wahrscheinlich performancemäßig ganz gut im Vergleich sein und eventuell ein Vorteil haben gegenüber Basic-Interpretern. Es ist natürlich ein Apple zu 40-alte-Jahre-alte-Pflaumen- Vergleich und kein Apple zu Orangen-Vergleich. Aber die Applesoft-Implementierung und die Basic-Implementierung, die hier implementiert wurde, sind die die nächste Frage ist von ProLest. Denkst du, dass Racket mit dem flexiblen Macrosystem besonders gut für das Bauen von Sprachen gedacht ist? Ja, es ist das beste System in der Welt, um Sprachen zu bauen. Und in meiner kommerziellen Arbeit, wenn wir eine DSL, eine domain-spezifische Sprache Racket meistens involviert in der Design-Stufe und häufig auch in der Produktionsstufe. Racket ist eine Nachkomme von Lisp und wenn man also mit einem traditionellen Lisp wie Clojure arbeitet, das hat auch ein Macrosystem, aber die Macrosysteme von traditionellen Lisp sind wesentlich weniger stark und machtvoll wie in Racket. Und da gibt's zwei Unterschiede. Die Syntax und die traditionellen Lisp ist nicht so gut, um die Quellcode-Information zu erhalten, was in vielen domain-spezifischen Sprachen nicht so gut geeignet ist und das andere ist, es arbeitet sehr gut mit Hygiene. Da haben wir im Talk nicht drüber geredet, weil da gibt es sehr viele Talks und Papers drüber über Hygiene, aber das häufig so, dass wenn man ein Macro schreibt, muss man neue Namen erfinden, die nur temporär sein sollen. Das muss man immer sehr aufpassen in einem normalen Lisp-System, während es in Racket komplett automatisch ist. Und die Subtilitäten, die ich gezeigt habe, das ist eigentlich alles, worauf man groß aufpassen muss. Ich glaube nicht, dass es da viel mehr gibt, wo ich von Tag zu Tag viel mitarbeiten muss. Und im traditionellen Lisp-System müsste man sowieso auch viele Gedanken über Hygiene machen. Und das gibt einem automatisch einen Machtlevel mehr mit einem Lisp-System, darüber wie crazy Sachen man machen kann mit seinem Lisp-System.