Ich entwickle eine DSL und ich bekomme einen "freien Begriff" Fehler beim Erweitern eines Makros. Ich würde gerne wissen, ob es vermieden werden kann. Ich habe das Problem auf die folgende Situation vereinfacht.
Angenommen, wir haben diesen Ausdruck:
%Vor%wo join ein Makro ist, dessen Implementierung ist:
%Vor%Das Ziel des Makros besteht darin, alle Elemente im Argumentblock zu verbinden und sie in einer einzigen Liste zurückzugeben. Da der Inhalt des Blocks variabel sein kann, kann ich das kommentierte Verify nicht verwenden (was gut funktioniert). Die unkommentierte - mit einem für das Verständnis, die freie Begriffe erzeugt - wirft die Nachricht:
"Die Makroerweiterung enthält eine freie Variablenliste, die durch Join in Macros.scala definiert ist: 48: 18. Haben Sie vergessen, eval zu verwenden, wenn Sie diese Variable in einen Verweis einbetten? mit -Xlog-free-terms "
Gibt es eine Möglichkeit, das Verständnis (oder einen Iterator oder was auch immer) einzuführen, ohne diesen Fehler zu bekommen? Übrigens benutze ich 2.10-M3.
Das Problem besteht darin, dass Ihr Code Kompilierungszeit- und Laufzeitkonzepte mischt.
Die von Ihnen verwendete "list" -Variable ist ein Kompilierzeitwert (dh sie sollte während der Kompilierzeit iteriert werden), und Sie möchten, dass es bis zur Laufzeit beibehalten wird (durch Spleißen abgeleiteter Werte) ). Dieses rätselhafte Rätsel führt zur Schaffung eines so genannten freien Begriffs.
Kurz gesagt, freie Begriffe sind Stubs, die sich auf die Werte früherer Stufen beziehen. Zum Beispiel das folgende Snippet:
%Vor%Würde wie folgt kompiliert:
%Vor%Clever, nicht wahr? Das Ergebnis behält die Tatsache bei, dass x ein Ident ist, behält seinen Typ (Kompilierzeitmerkmale) bei, bezieht sich jedoch auch auf seinen Wert (ein Laufzeitmerkmal). Dies wird durch lexikalisches Scoping ermöglicht.
Aber wenn Sie versuchen, diesen Baum von einer Makro-Erweiterung (die in die Aufruf-Site eines Makros eingezeichnet ist) zurückzugeben, werden die Dinge in die Luft gehen. Call-Site des Makros wird höchstwahrscheinlich nicht x in seinem lexikalischen Bereich haben, so dass es nicht in der Lage wäre, sich auf den Wert von x zu beziehen.
Was noch schlimmer ist. Wenn das obige Snippet in ein Makro geschrieben wird, existiert x nur während der Kompilierungszeit, d. H. In der JVM, die den Compiler ausführt. Aber wenn der Compiler beendet ist, ist es weg.
Die Ergebnisse der Makroexpansion, die einen Verweis auf x enthalten, sollen jedoch zur Laufzeit ausgeführt werden (am wahrscheinlichsten in einer anderen JVM). Um dies zu verstehen, würden Sie eine stufenübergreifende Persistenz benötigen, d. H. Eine Fähigkeit, willkürliche Werte für die Kompilierungszeit irgendwie zu serialisieren und sie während der Laufzeit zu deserialisieren. Ich weiß nicht, wie ich das in einer kompilierten Sprache wie Scala machen soll.
Beachten Sie, dass in einigen Fällen eine Kreuzstufenpersistenz möglich ist. Zum Beispiel, wenn x ein Feld eines statischen Objekts ist:
%Vor%Dann würde es nicht als freier Begriff enden, sondern würde auf direkte Art und Weise verdinglicht werden:
%Vor%Dies ist ein interessantes Konzept, das auch in SPJs Vortrag auf den Scala Days 2012 diskutiert wurde: Ссылка .
Um zu überprüfen, dass einige Ausdrücke keine freien Ausdrücke enthalten, fügen sie in Haskell dem Compiler ein neues, eingebautes Primitiv hinzu, den Konstruktor Static
type. Mit Makros können wir dies natürlich tun, indem wir reify verwenden (was selbst nur ein Makro ist). Siehe die Diskussion hier: Ссылка .
Okay, jetzt haben wir gesehen, was genau das Problem mit dem ursprünglichen Code ist, also wie machen wir es funktionieren?
Leider müssen wir auf die manuelle AST-Konstruktion zurückgreifen, da es für retify schwierig ist, dynamische Bäume auszudrücken. Der ideale Anwendungsfall für die Verifikation in der Makrologie ist eine statische Schablone mit den Typen der Löcher, die zur Makrokompilierungszeit bekannt sind. Machen Sie einen Schritt zur Seite - und Sie müssen Bäume mit der Hand bauen.
Das Endergebnis ist, dass Sie mit den folgenden Punkten arbeiten müssen (funktioniert mit dem kürzlich veröffentlichten 2.10.0-M4, siehe den Migrationsleitfaden in scala-language, um zu sehen, was genau geändert wurde: Ссылка ):
%Vor%Tags und Links macros scala-2.10