SP005: Macros, Compiler und Web-Geschichten
Erste Erfahrungen mit Macros
Endlich hat Sven selber Erfahrungen mit Macros gesammelt. Auslöser waren erste Gehversuche mit Scala.js zur Umsetzung eines AngularJS-Projekts auf Basis von scalajs-angular.
Scala.js bietet nahezu alle Möglichkeiten von Scala, aber Reflection gehört nicht dazu. Probleme, die in Java üblicherweise über Reflection gelöst werden, lassen sich in Scala.js aber mit Macros meist eleganter und vor Allem zur Compilier-Zeit lösen.
scalajs-angular definiert zum Beispiel eine @injectable
-Annotation um in Scala.js geschriebenen Komponenten den von Angular für Dependency-Injection benötigten Namen zuzuweisen. Darauf basierend hat Sven einige nützliche Macros erstellt:
- Methode um sich vom
Injector
eine Instanz eines Typs liefern zu lassen ohne manuell den Namen angeben und dann auch noch explizit casten zu müssen (PR #79) - Methoden
relativeTemplate
undcompanionTemplate
, um in HTML-Dateien abgelegte Templates zur Compile-Zeit alsString
einbetten zu können (PR #83)
Erfahrungen
- Wenn man eine Vorlage hat ist die Erstellung eines Makros mit
reify
oder Quasiquotes relativ einfach. - Ohne Vorlage ein Macro zu erstellen ist für mich weiterhin schwierig.
- Auch nach dem Schreiben erschließen mir sich diverse Aspekte nicht (Beispiel
splice
) — hier ist wohl doch ein tiefergreifendes Verständnis des ASTs und der zugehörigen API notwendig. Gibt es da irgendwo eine brauchbar Beschreibung? - Macros können nur auf Objekten definiert werden — nicht auf Klassen. Um auf Instanzen zu operieren sind also Hilfs-Objekte notwendig — implizite Parameter können die Nutzung vereinfachen.
- Die notwendige Verteilung von Macros und Nutzung auf zwei Projekte macht die Erstellung von Libraries mit Komfort-Funktionen schwierig.
Mehr zu meinen Erfahrungen mit Scala.js in der nächsten Episode.
SourceCode
Kleine Scala Bibliothek, die Zugriff auf Source-Code Informationen wie z.B. den Dateinamen oder der Zeile zur Laufzeit bietet. Die benötigten Objekte können dazu über implizite Parameter oder implicitly
geholt werden.
User-Cases
- Logging
def log(message: String)(implicit line sourcecode.Line, file sourcecode.File): Unit = println(s"[${file.value}:${line.value}]: $message")
- Übernehmen von Namen
val CREATION_DATE = newField("CREATION_DATE", ...)
=>
val CREATION_DATE = newField(...)
- Debug Ausgaben
def log[A](value: sourcecode.Text[V])(implicit name sourcecode.Name): Unit = println(s"${name.value}(${value.source} = ${value.value})") def foo(arg: String): Unit = { log(arg) ... } foo("bar")
Gibt aus:
foo(arg = bar)
Links
Dotty
- Compiliert den größten Teil er Scala Standard Library und eine handvoll anderer Projekte
- Ziel ist es einen neuen, schlanken Compiler zu erhalten, der sich deutlich leichter weiterentwickeln lässt (wie der Roslyn Compiler für C#)
- Das Vorgehen über einen experimentellen Compiler hat sich ebenfalls bei Roslyn bewährt (der übrigens laut eigenen Aussagen einiges von Scala übernommen hat)
- Ob dotty irgendwann scalac ablösen wird ist nicht klar — es kann auch sein, dass lediglich Erkenntnisse aus dotty nach scalac zurückfließen.
Scala 2.12.0‑M4
Vervollständigt die Features von 2.12. Auffälligste Änderung ist der neue ScalaDoc-Style.
Es folgen M5 und danach RC1.
Scala 2.12 Features
In Scala 2.12 geht es im Wesentlichen darum die neuen Java 8 Features optimal zu nutzen:
- Traits zu Interfaces compilieren (bisher Klasse und Interface)
- Java 8‑styled Lambdas:
- Keine anonymen Klassen mehr und somit deutlich kleinere JARs.
- Bessere Java-Scala interoperabilität
- Neues Compiler-Backend arbeitet schneller
- Neuer Bytecode-Optimizer (unter anderem Inlining von
final
Methoden)
Links
SI-2712
Links
Frontend Asset-Pipeline mit Play
sbt-web
verspricht mit Web-JARs die einfache Einbindung von JavaScript-Libraries und mit Plug-Ins einen einfachen Aufbau einer Frontend-Asset-Pipeline, um z.B. LESS- oder SASS-Dateien zu CSS zu compilieren, ES6-JavaScript nach ES5 zu compilieren, zu konkatenieren und zu minifizieren.
In der Praxis klappt das leider nicht: Die sbt-web
-Plug-Ins sind halbherzig umgesetzt:
- Optionen fehlen oder funktionieren nicht wie versprochen
- sinnvolle Verarbeitungsketten (z.B. mit der Erstellung von Source-Maps über mehrere Schritte hinweg) lassen sich gar nicht realisieren.
Ausweg
Mit sbt-web
-Plug-Ins wie zum Beispiel sbt-play-gulp lässt sich eine Asset-Pipeline mit im JavaScript-Umfeld etablierten Build-Systemen (in diesem Fall GULP) aufbauen und in de Play-Lifecycle einbinden.
ScalaCSS
Scala Bibliothek zum Erzeugen von CSS, ähnlich wie LESS oder SASS, allerdings mit Scala-Code.
Browser Prefix
Bei CSS-Attributen oder Funktionen bei denen für die verschiedenen Browser ein Prefix verwendet werden muss kümmert ScalaCSS sich automatisch darum die Prefixe für alle Browser gesetzt werden.
Konflikterkennung
// In MyGenericTheme.scala
val button = style(margin(8 px, auto), ...)
// In UserStyles.scala
val userTitle = style(marginLeft(4 ex), ...)
...
val userButton = style(button, userTitle, ...)
ScalaCSS produziert hier auf Wunsch eine Warnung und es kann festgelegt werden, welcher Stil gewinnen soll.
Links
Titelsong basierend auf Wish You Were Here von THE.MADPIX.PROJECT lizensiert unter Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported (CC BY-NC-SA 3.0).