Scala (Programmiersprache)
Scala ist eine funktionale und objektorientierte Programmiersprache. KonzepteIntegration mit JavaScala-Programme können Java Archive (umgangssprachlich JAR-Datei) ansprechen und Java-Programme können Scala-Dateien ansprechen.[6] Es können also alle bestehenden Java-Bibliotheken und -Frameworks in Scala-Projekte eingebunden und dort genutzt werden. Umgekehrt ist das zwar prinzipiell möglich, in der Praxis aber nicht immer problemlos. Ähnliches gilt für die meisten Werkzeuge: Entwicklungsumgebungen wie Eclipse, NetBeans oder IntelliJ unterstützen Scala ebenfalls. ObjektorientierungScala ist, anders als Java, eine rein objektorientierte Programmiersprache. Jeder Wert ist ein Objekt. Das gilt auch für primitive Datentypen, ohne dass es zu Performance-Einbußen kommt, denn der vom Compiler erzeugte Bytecode verwendet primitive Datentypen. Interfaces werden über den Mechanismus der Traits implementiert. Traits bestehen nicht nur aus Definitionen, sie können bereits konkrete Implementierungen von Methoden enthalten. Klassen können einen oder mehrere Traits erweitern (Schlüsselwort Über das Schlüsselwort Funktionale SpracheFunktionen sind First-Class-Objekte. Sie können an allen Stellen verwendet werden, an denen Werte erlaubt sind, z. B. Zuweisung an eine Variable (hier ist nicht das Ergebnis der Funktionsauswertung gemeint, sondern die Funktionen selbst) oder bei der Parameterübergabe. Methoden sind selbst aber keine First-Class-Objekte, können aber jederzeit in Funktionen umgewandelt werden. Auch Funktionen höherer Ordnung[8] sind in Scala realisiert, wodurch beispielsweise Currying ermöglicht wird. Pattern MatchingEin wichtiger Aspekt zur Unterstützung der funktionalen Programmierung mit Scala ist Pattern Matching. Im Gegensatz zu der switch-Anweisung, wie sie zum Beispiel in Java implementiert ist, arbeitet Pattern Matching nicht nur auf der Basis von Werten, sondern auch bezogen auf die Struktur bzw. den Typ eines Objektes. Um auf eine Instanz Pattern Matching anwenden zu können, muss es für sie ein Singleton-Objekt geben, das die unapply-Methode[9] implementiert. So kann man Werte aus der Klasse extrahieren, die entsprechend der unapply-Methode von den Feldern der Klasse abgeleitet werden. Da man oft nur die Felder selbst extrahieren muss, gibt es sogenannte Case Classes[10] in Scala. Bei ihnen generiert der Scala-Compiler automatisch und vom Programmierer unbemerkt ein gleichnamiges Singleton-Objekt mit apply- und unapply-Methode. Der folgende Code implementiert die Suche in einem binären Suchbaum mithilfe von Pattern Matching und Case Classes: enum Tree:
case Leaf(key: Int)
case Branch(key: Int, left: Tree, right: Tree)
import Tree._
def contains(tree: Tree, key: Int): Boolean = tree match
case Leaf(i) => i == key
case Branch(i, _, _) if i == key => true
case Branch(i, left, _) if i > key => contains(left, key)
case Branch(i, _, right) => contains(right, key)
Beispielaufruf: val sorted: Tree = Branch(4, Leaf(2), Branch(7, Leaf(6), Leaf(8)))
println(s"contains(sorted, 4) -> ${contains(sorted, 4)}")
println(s"contains(sorted, 5) -> ${contains(sorted, 5)}")
println(s"contains(sorted, 6) -> ${contains(sorted, 6)}")
// Ausgabe:
// contains(sorted, 4) -> true
// contains(sorted, 5) -> false
// contains(sorted, 6) -> true
ClosuresFunktionen greifen nicht nur auf ihre Parameter und lokalen Variablen zu, sondern auch auf Variablen ihres Kontextes (Scope), welche zum Auswertungszeitpunkt gültig sind. Dadurch werden aus open terms die namensgebenden closed terms. Falls sich bei mehrfacher Verwendung der Funktion der Wert einer Variablen des Kontextes gegenüber einem früheren Auswertungszeitpunkt ändert, kann sich auch der Rückgabewert und das Verhalten der Funktion ändern. TypsystemScala ist statisch typisiert.
Generische Klassen verwenden Typen, die zum Entwicklungszeitpunkt noch nicht festgelegt sind, z. B. Kovarianz und KontravarianzTypparameter einer generischen Klasse können mit einer Annotation versehen werden, die bestimmt, wie sich Untertyprelationen von Typargumenten auf die Untertyprelation von generischen Instanziierungen der Klasse auswirken. Invarianz, Syntax: TypinferenzTypinferenz ist die Fähigkeit des Compilers, den Typ eines Ausdrucks aus dem Kontext herzuleiten, welche unter Syntax beispielhaft dargestellt ist. AuswertungsstrategieFunktionale Ausdrücke werden in Scala strikt ausgewertet. Allerdings kann durch das Schlüsselwort XMLScala 2 unterstützt XML mittels Syntax (XML-Literale) und Standardbibliothek. Mit Version 2.13 wurde die XML-Unterstützung aus der Standardbibliothek entfernt und in das eigenständige Projekt scala-xml ausgelagert[13]. Mit Scala 3 wurde auch die Unterstützung für XML-Literale deprecated und wird somit künftig entfernt[14]. In der scala-xml-Bibliothek sind grundlegende XML-Operationen und -Datentypen verfügbar: XML-Konstruktoren, -Serialisierung und -Deserialisierung, XPath-ähnliche Extraktion von Elementen und Attributen: val liste = <einkaufsliste>
<artikel><name>Brot</name><kosten>3.50</kosten></artikel>
<artikel><name>Apfel</name><kosten>0.29</kosten></artikel>
<artikel><name>Eier</name><kosten>1.19</kosten></artikel>
</einkaufsliste>
val gesamtkosten = (liste \\ "kosten").map(_.text.toDouble).sum
// Ergebnis: 4.98
ImplicitsMethoden können mittels des Modifiers Die letzte Parameterliste einer Methode kann ebenfalls als implicit markiert werden. Wenn die Parameterliste beim Aufruf einer Methode fehlt, aber ein als implicit markierter Wert im lexikalischen Scope zu finden ist, wird er automatisch an die Methode übergeben. Hiermit ist es möglich, die aus Haskell bekannten type classes als Entwurfsmuster nachzubilden[15]. Scalaz, eine Library zur pur funktionalen Programmierung in Scala, setzt Typklassen weiträumig ein. Mit impliziten Konversionen wird in Scala auch Verhalten in Bibliotheken implementiert, das viele Sprachen als Spezialfall im Compiler abdecken. So zum Beispiel besondere Regeln beim Zusammenfügen von Zeichenketten wie NebenläufigkeitWährend Scala Threads durch die Java-Klassenbibliothek unterstützt, gibt es in Scalas eigener Bibliothek eine Implementierung von Aktoren. Diese wurde von der Aktoren-Implementierung, wie sie in Erlang umgesetzt wurde, inspiriert. Seit der Scala-Version 2.11 ist die ursprüngliche Aktoren-Implementierung nicht mehr Bestandteil der Standardbibliothek. Ersetzt wird sie durch die Akka-Implementierung (ab der Version 2.10 verfügbar)[18]. Zusätzlich implementiert die Standard-Library von Scala Futures und parallele Collections. SyntaxDie Syntax der Sprache ist an Java und ML angelehnt. Von Java wurde vor allem eine Reihe von Schlüsselworten sowie die Blocksyntax übernommen, von ML die Syntax für Typannotationen und Deklarationen. Im Vergleich zur Java-Syntax kann in den meisten Fällen das Semikolon am Ende einer Zeile entfallen. Die Syntax zur Typdefinition von Variablen und Rückgabewerten lehnt sich an der von ML statt von Java an: Man formuliert nicht Die Deklaration und Definition von Werten, Variablen und Methoden erfolgt mittels der Schlüsselwörter val wert: Int = 42
var variable: Double = 3.14
def methode(parameter1: String, parameter2: Boolean): Unit
Der Compiler leitet den Typ einer Variable aus dem Kontext ab (Typinferenz). Die beiden Zeilen var x = "Ein Text"
und var x: String = "Ein Text"
sind somit gleichwertig. Klassen- und Methodennamen können einen großen Umfang von Zeichen und Symbolen nutzen. Es sind z. B. Bezeichner wie Methodenaufrufe mit keinem oder einem Parameter können unter Auslassung des Punktes und der öffnenden und schließenden runden Klammern notiert werden (ähnlich wie in Smalltalk oder Objective-C): 5.0 + 2.0
"Test" startsWith "T"
List(1,2,3) isEmpty
entspricht 5.0.+(2.0)
"Test".startsWith("T")
List(1, 2, 3).isEmpty
Mit Scala ist es außerdem möglich, den Quelltext im Vergleich zu Java in vielen Fällen kompakter zu schreiben, zum Beispiel auf Grund von Typinferenz, for comprehensions oder anonymen Funktionen. OperatorenFür Präfix-Operatoren gibt es eine fest vorgegebene Menge, nämlich Postfix-Operator-Ausdrücke sind ebenfalls möglich. Hier gibt es keine Einschränkungen an den Operator, und das Übersetzungsergebnis ist ein Aufruf der (parameterlosen) Methode auf dem Operanden. Bei Infix-Operatoren entscheidet das erste Zeichen des Operatornamens über Präzedenz und Assoziativität, das den aus der Mathematik üblichen Konventionen folgt. Das Codefragment 1 + z * x
wird übersetzt zu (1).+(z.*(x))
Auf dem Objekt Endet der Methodenname eines Infixoperators mit einem Doppelpunkt, so vertauscht sich die Reihenfolge von Empfänger und Parameter und der Operator ist rechtsassoziativ: a :: b
wird übersetzt zu b.::(a)
SchleifenFor-Schleifen wurden zu sogenannten for comprehensions soweit generalisiert, dass sie nicht nur mehrere verschachtelte Schleifen zusammenfassen, sondern analog zu Haskells Do-Notation beliebige Monaden nutzen können. Dieser Code gibt beispielsweise 27 Zeilen für jeden Wert von a, b und c aus. for
a <- List(1, 2, 3)
b <- List(2, 3, 4)
c <- List(5, 6, 7)
do println("a=" + a + ", b=" + b + ", c=" + c)
Eine for comprehension kann auch genutzt werden, um neue Werte zu berechnen, ähnlich wie mit den von Haskell bekannten List Comprehensions. Dieser Code weist val combinations = for
a <- List(1, 2)
b <- List(3, 4)
yield (a, b)
VersionenScala 2.8Wesentliche Neuerungen im Release 2.8[19] sind:
Scala 2.9Wesentliche Neuerung der Version 2.9 ist die Erweiterung der Collection-Bibliothek um Methoden und Klassen, die Operationen parallel ausführen können (scala.collection.parallel).[23] Daneben gibt es zahlreiche weitere Verbesserungen:
Scala 2.10Wesentliche Neuerungen der Version 2.10.0[25]:
Scala 2.11Die Version 2.11.0 wurde am 17. April 2014 veröffentlicht und ist gegenüber den Vorgängern stärker modularisiert, wodurch die Kern-Standardbibliothek kleiner wird. Zudem wurden Detailverbesserungen im Bereich der Geschwindigkeit gemacht und die nach wie vor experimentellen Bereiche von Makros und Reflection verbessert.[26] Scala 2.12Die Version 2.12.0 wurde am 3. November 2016 veröffentlicht.[27] Scala 3.0Die Version 3.0.0 wurde am 14. Mai 2021 veröffentlicht. Nennenswerte Änderungen umfassen:[28]
Bibliotheken und FrameworksBeliebte Frameworks zur Entwicklung von Web-Applikationen sind Play und Lift. Daneben gibt es viele weitere, meist eher minimalistische Lösungen wie Finatra oder Scalatra. Frameworks aus der Java-Welt, wie Wicket oder Spring können ebenfalls genutzt werden. Die Interaktion mit Datenbanken wird durch eine Vielzahl von Bibliotheken ermöglicht, darunter Slick, Squeryl und ScalikeJDBC. In Java populäre Ansätze, wie die Nutzung von JPA oder JOOQ, sowie die direkte Verwendung von JDBC, sind ebenfalls möglich. Zur nebenläufigen Programmierung bietet Scalas Standardbibliothek eine Futures & Promises API.[29] Implementierungen des Aktormodells werden u. a. von Akka[30] und Scalaz[31] bereitgestellt.
Darüber hinaus können auch alle Möglichkeiten der Java-Standardbibliothek genutzt werden, z. B. Threads oder Scalaz enthält außerdem viele weitere Konstrukte, welche die funktionale Programmierung in Scala erleichtern. Scala.js ist ein Projekt, das Scala-Code zu JavaScript-Code kompilieren und Scala somit im Browser ausführbar machen kann.[3] IDE- und WerkzeugunterstützungNeben dem Compiler Für den Erstellungsprozess unterstützt Scala u. a. Ant und Maven, stellt aber auch ein eigenes Werkzeug, SBT zur Verfügung.[35] VerwendungScala hat mittlerweile Anwendung in der Industrie gefunden. Die sozialen Netzwerke Twitter und LinkedIn haben ihre Nachrichten-Warteschlangen in Scala implementiert.[36][37] Weitere Verwendung findet die Sprache etwa in Unternehmen wie Novell[38], Siemens, Sony oder Électricité de France Trading.[39] NamensherkunftDer Name leitet sich von „scalable language“ ab und bringt zum Ausdruck, dass der sehr kompakt gehaltene Sprachkern die Möglichkeit bietet, häufig verwendete Sprachelemente wie z. B. Operatoren oder zusätzliche Kontrollstrukturen in Benutzerklassen zu implementieren und dadurch den Sprachumfang zu erweitern und eigene domänenspezifische Sprachen (englisch domain-specific language, DSL) zu erstellen. BeispieleEin Hello, World!-Programm in Scala: @main def hello =
println("Hello, world!")
Eine generische Implementierung des Quicksort-Algorithmus mit Context Bounds[40]: import math.Ordering.Implicits.infixOrderingOps
def quickSort[A : Ordering](xs: List[A]): List[A] = xs match
case Nil => Nil
case y :: ys =>
val (l1, l2) = ys partition (_ <= y)
quickSort(l1) ++ (y :: quickSort(l2))
GeschichteScala wird im Labor für Programmiermethoden an der École polytechnique fédérale de Lausanne in der Schweiz unter der Leitung von Martin Odersky entwickelt. Martin Odersky arbeitete unter Niklaus Wirth an Modula-2 und Oberon. Von 1995 an entwickelte er zusammen mit Philip Wadler die inzwischen aufgegebene Programmiersprache Pizza[41], die Java um Generics, Funktionszeiger und Pattern Matching erweiterte. Später konzentrierten sich Wadler und Odersky mit Generic Java[42] (GJ) auf Generics für Java, dieses Projekt führte 2004 zur Einführung von Generics in Java.[43] Ab 1999 arbeitete Martin Odersky an der École polytechnique fédérale de Lausanne, wo er an der Verbindung funktionaler und objektorientierter Programmierung forschte und die minimalistische Hybridsprache Funnel[44] entwickelte. Hier begann er 2001 mit der Entwicklung von Scala, die im Gegensatz zu Funnel nicht rein akademischem Interesse dienen sollte, sondern als vollwertige Sprache für reale Anwendungen ausgelegt war. Im Frühjahr 2004 wurde Scala für die Java-Plattform veröffentlicht, im Juni 2004 für das .Net-Framework. Seit Anfang 2011 wird die Weiterentwicklung der Sprache vom European Research Council finanziell gefördert. Damit sollen insbesondere die Möglichkeiten der parallelen Programmierung ausgebaut werden.[45] Am 12. Mai 2011 gab Martin Odersky den Start von Typesafe bekannt; ein Unternehmen, das sich dem kommerziellen Support von Scala im Allgemeinen sowie dem Middleware Framework Akka widmet. Zu den Beratern gehören James Gosling und Doug Lea.[46] Literatur
Weblinks
Einzelnachweise
|
Portal di Ensiklopedia Dunia