© Ungvari Attila/Shutterstock.com
Was uns an Änderungen in der neuen Version erwartet

Die Top 5 der Neuerungen in Scala 3


Schon seit geraumer Zeit arbeitet das Team um Martin Odersky an der nächsten großen Version von Scala. Deren Spitzname „Dotty“ leitet sich ab von „Dependent Object Types“, dem logischen Kalkül, welches dem Typsystem zugrunde liegt. Doch es gibt nicht nur Verbesserungen in den Eingeweiden des Compilers. Die aktuell in der Release-Candidate-Phase befindliche Vorschau auf Scala 3 verspricht, das Programmiererlebnis deutlich zu verbessern.

In diesem Artikel möchte ich meine völlig willkürlich ausgewählten Top 5 der Neuerungen vorstellen. Diese und alle weiteren Änderungen sind ausführlich auf der Dotty-Webseite dokumentiert [1].

Platz 5: opake Typen

Wer kennt das Problem nicht? Man muss im Code physikalische Größen verwalten – zum Beispiel Zeiten – und verwechselt die Einheiten. Durch welche Zehnerpotenz muss man System.nanoTime() dividieren, um auf Sekunden zu kommen? Typischerweise sind Größen im Code als Int oder Double repräsentiert, sodass der Compiler kaum Hilfestellung geben kann, wenn man sich verrechnet. Dieses Problem ist in der Praxis derart häufig, dass es teilweise sogar in rigoros analysiertem Code nicht aufgespürt wird [2].

In vielen funktionalen Programmiersprachen steht eine elegante Lösung dafür zur Verfügung: Man definiert kurzerhand einen Wrapper-Typen, der selbst zwar auch nur ein Double enthält, aber explizite Konvertierungsmethoden anbietet. In Scala 2 kann das zum Beispiel so aussehen:

object Time { case class Milliseconds(num: Int) case class Seconds(num: Int) { def toMillis: Milliseconds = Milliseconds(num * 1000) } }

So ähnlich wurde die Modellierung von Zeiträumen auch in der Standardbibliothek für Timeouts von asynchronen Operationen umgesetzt.

Während Scala hier die Vorteile seiner konzisen Syntax gegenüber Java voll ausspielen kann, ist diese Art der Programmierung der Performance oft nicht zuträglich. Denn müssen viele solcher Objekte erzeugt werden, wird ständig geboxt: Ein Array[Seconds] benötigt viel mehr Speicher als ein Array[Int], denn bekanntlich macht Kleinvieh auch Mist.

Scala 2 hat sogenannte Value Classes eingeführt, die aber das Boxing-Problem in bestimmten Situationen nicht lösen konnten. Hier setzt jetzt Scala 3 mit den sogenannten Opaque Type Aliases an. Innerhalb eines Objekts lassen sich mehrere Synonyme definieren, einschließlich Methoden zur sicheren Konvertierung:

object Time { opaque type Milliseconds = Int opaque type Seconds = Int def seconds(num: Int): Seconds = num extension (num: Seconds) def toMillis: Milliseconds = num * 1000 }

Das besondere hieran ist, dass die Identität Milliseconds = Int nur innerhalb des Objekt-Scopes gegeben ist, sodass man die Konvertierungsmethoden direkt auf Int implementieren kann.

Außerhalb des Scopes kann man aber nur auf die vorgegebenen Methoden zurückgreifen. Im Gegensatz zu den alten Value Classes sind folglich die Implementierungsdetails standardmäßig versteckt. So ist es im obigen Beispiel nicht möglich, ein Milliseconds-Wert ohne Umweg über Seconds zu konstruieren. Stattdessen geht man über Sekunden:

scala> Time.seconds(10).toMillis val res0: Time.Milliseconds = 10000

Dabei sind die Opaque Type Aliases nicht bloß syntaktischer Zucker; sondern der Compiler garantiert auch, dass sie zur Laufzeit nicht existieren. Es werden also weder das JAR-File noch der Heap aufgebläht, wenn man großzügig Aliase benutzt.

Besonders interessant sind die Aliase also für Domain-driven Design, denn damit lassen sich fachliche Typen wie Adressen, Postleitzahlen oder Namen so modellieren, dass eine Verwechslung von vornherein ausgeschlossen wird.

Platz 4: aufgeräumte Implicits

Der Codeschnipsel vom vorherigen Platz enthält übrigens noch...

Neugierig geworden? Wir haben diese Angebote für dich:

Angebote für Gewinner-Teams

Wir bieten Lizenz-Lösungen für Teams jeder Größe: Finden Sie heraus, welche Lösung am besten zu Ihnen passt.

Das Library-Modell:
IP-Zugang

Das Company-Modell:
Domain-Zugang