24
Arbeitsgruppe für Programmiersprachen und Übersetzerkonstruktion Institut für Informatik Christian-Albrechts-Universität zu Kiel Seminararbeit Die multiparadigmatische Programmiersprache Scala Lasse Kristopher Meyer Wintersemester 2012/2013 Betreut von Björn Peemöller

Die multiparadigmatische Programmiersprache Scalamh/lehre/bachelor_seminar...1In Scala können Methoden in Operatorenschreibweise definiert werden. Eine äquivalente Schreibweise

Embed Size (px)

Citation preview

Page 1: Die multiparadigmatische Programmiersprache Scalamh/lehre/bachelor_seminar...1In Scala können Methoden in Operatorenschreibweise definiert werden. Eine äquivalente Schreibweise

Arbeitsgruppe für Programmiersprachen und ÜbersetzerkonstruktionInstitut für InformatikChristian-Albrechts-Universität zu Kiel

Seminararbeit

Die multiparadigmatische Programmiersprache

ScalaLasse Kristopher Meyer

Wintersemester 2012/2013

Betreut von Björn Peemöller

Page 2: Die multiparadigmatische Programmiersprache Scalamh/lehre/bachelor_seminar...1In Scala können Methoden in Operatorenschreibweise definiert werden. Eine äquivalente Schreibweise

Inhaltsverzeichnis ii

Inhaltsverzeichnis

Inhaltsverzeichnis ii

Abbildungsverzeichnis iii

Tabellenverzeichnis iii

Listings iii

1 Einordnung 11.1 Grundlegende Eigenschaften . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1

1.1.1 Scala als objektorientierte Programmiersprache . . . . . . . . . . . . . . . 11.1.2 Scala als funktionale Programmiersprache . . . . . . . . . . . . . . . . . . 2

1.2 Geschichte und Entwicklung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21.3 Einflüsse anderer Programmiersprachen . . . . . . . . . . . . . . . . . . . . . . . 3

2 Anwendungsgebiete 52.1 Wofür ist Scala beabsichtigt? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52.2 Wo wird Scala bereits eingesetzt? . . . . . . . . . . . . . . . . . . . . . . . . . . . 6

2.2.1 Beispiel 1: Twitter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62.2.2 Beispiel 2: Play Framework . . . . . . . . . . . . . . . . . . . . . . . . . . 6

3 Ausgewählte Konzepte 73.1 Allgemeine Konzepte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7

3.1.1 Variablen und Funktionen . . . . . . . . . . . . . . . . . . . . . . . . . . . 73.1.2 for-Ausdrücke . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8

3.2 Objektorientierte Konzepte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93.2.1 Grundlagen, Klassen und Vererbung . . . . . . . . . . . . . . . . . . . . . 93.2.2 Singleton-Objekte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113.2.3 Traits . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12

3.3 Funktionale Konzepte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133.3.1 Grundlagen, Funktionen und Closures . . . . . . . . . . . . . . . . . . . . 133.3.2 Case Classes, generische Typen und Pattern Matching . . . . . . . . . . . . 153.3.3 Aktoren und Nebenläufigkeit . . . . . . . . . . . . . . . . . . . . . . . . . 16

4 Technische Unterstützung 184.1 Integration mit Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 184.2 Compiler, Interpreter, IDEs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 184.3 Der Typesafe Stack . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19

5 Diskussion und Zusammenfassung 20

Literaturverzeichnis 21

Lasse Kristopher Meyer

Page 3: Die multiparadigmatische Programmiersprache Scalamh/lehre/bachelor_seminar...1In Scala können Methoden in Operatorenschreibweise definiert werden. Eine äquivalente Schreibweise

Abbildungsverzeichnis/Tabellenverzeichnis/Listings iii

Abbildungsverzeichnis

4.1 Der Scala-Interpreter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18

Tabellenverzeichnis

1.1 Auszug aus der Versionsgeschichte Scalas . . . . . . . . . . . . . . . . . . . . . . 31.2 Einflüsse anderer Programmiersprachen auf den Entwurf Scalas . . . . . . . . . . 4

Listings

3.1 Filtern mit for-Ausdrücken . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83.2 Rückgabewerte von for-Ausdrücken . . . . . . . . . . . . . . . . . . . . . . . . . 83.3 Complex.scala (1. Teil) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93.4 Abstrakte Klassen und Vererbung . . . . . . . . . . . . . . . . . . . . . . . . . . . 103.5 Complex.scala (2. Teil) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113.6 Singleton-Objekte und Vererbung . . . . . . . . . . . . . . . . . . . . . . . . . . . 113.7 Abstrakte und konkrete Elemente in Traits . . . . . . . . . . . . . . . . . . . . . . 123.8 Stapelbare Modifikationen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123.9 Closures . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143.10 Endrekursive Funktionen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 153.11 Tree.scala (1. Teil) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 153.12 Tree.scala (2. Teil) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163.13 Aktoren implementieren (Das Actor-Trait) . . . . . . . . . . . . . . . . . . . . . . 163.14 Aktoren implementieren (Die actor-Methode) . . . . . . . . . . . . . . . . . . . 17

Lasse Kristopher Meyer

Page 4: Die multiparadigmatische Programmiersprache Scalamh/lehre/bachelor_seminar...1In Scala können Methoden in Operatorenschreibweise definiert werden. Eine äquivalente Schreibweise

1 Einordnung 1

1 Einordnung

Ziel des ersten Kapitels ist die Vermittlung grundlegender Eigenschaften der Programmierspra-che Scala und deren Einordnung. Zu diesem Zweck soll die Bedeutung der Programmierpara-digmen Scalas hervorgehoben werden. Die zwei letzten Abschnitte erläutern anschließend dieHintergründe der Entwicklungsgeschichte und die Einflüsse anderer Sprachen auf den Sprach-entwurf.

1.1 Grundlegende Eigenschaften

Scala ist eine objektorientierte (imperative), funktionale und statisch getypte Programmierspra-che. Der Name Scala ist ein Kofferwort und steht für „scalable language“. Die Wahl dieses Na-mens beruht auf dem Gedanken Scala den Ansprüchen und Anforderungen des Nutzers anpas-sen zu können. Dabei kann Scala einerseits als Skriptsprache verwendet werden, andererseitsbietet es aber auch Programmierkonstrukte ähnlich denen in Java, die die Entwicklung großerProjekte unterstützen sollen. Zu diesem Zweck ist der Sprachkern Scalas vergleichsweise kleingehalten. Vielmehr kann Scala manuell um die notwendigen Funktionen erweitert werden. Da-zu stehen dem Nutzer unter anderem bekannte objektorientierte Konzepte wie Vererbung zurVerfügung, gleichzeitig bietet Scala aber auch die Möglichkeit funktionale Elemente wie Funktio-nen höherer Ordnung zu verwenden, um beispielsweise eigene Kontrollstrukturen zu entwerfen.Laut den Entwicklern selbst resultiert die Skalierbarkeit der Programmiersprache Scala aber vorallem aus der Kombination objektorientieter und funktionaler Programmierung [OSV10], wes-halb diese beiden Aspekte im Folgenden näher beleuchtet werden.

1.1.1 Scala als objektorientierte Programmiersprache

Scala ist, ähnlich wie die Programmiersprache Smalltalk, rein objektorientiert. Das bedeutet,dass in Scala ausnahmslos jeder Wert ein Objekt ist und jede Operation ein Methodenaufruf.So ist die Addition 4 + 2 beispielsweise als ein Aufruf der Methode + des Objekts 4 vom TypInt mit dem Parameter 2 zu verstehen.1 Im Gegensatz zu Java kennt Scala keinen Unterschiedzwischen primitiven Datentypen und anderen Objekten, wenngleich Scalas Werttypen bei derUmwandlung in Java-Bytecode auf Javas primitive Datentypen zurückgeführt werden. Nebenbekannten Konzepten des objektorientierten Programmierens unterstützt Scala auch neuartigereobjektorientierte Programmiertechniken wie die Verwendung von Traits (s. Abschnitt 3.2.3).

1In Scala können Methoden in Operatorenschreibweise definiert werden. Eine äquivalente Schreibweise ist(4).+(2).

Lasse Kristopher Meyer

Page 5: Die multiparadigmatische Programmiersprache Scalamh/lehre/bachelor_seminar...1In Scala können Methoden in Operatorenschreibweise definiert werden. Eine äquivalente Schreibweise

1 Einordnung 2

1.1.2 Scala als funktionale Programmiersprache

Da Scala ebenso funktional ist und als logische Konsequenz aus der Tatsache, dass in Scala jederWert ein Objekt ist, sind auch Funktionen Objekte. Als First-Class-Objekte können FunktionenParameter oder Rückgabewerte sein, sie können wie andere Werte als Literale notiert werdenund in Variablen gespeichert oder innerhalb anderer Funktionen definiert werden. Diese Ei-genschaften ermöglichen beispielsweise die Definition eigener Kontrollstrukturen, was in denStandard-Bibliotheken Scalas auch konsequente Verwendung findet (z. B. bei der Implemen-tierung des Actor Models, s. Abschnitt 3.3.3). Neben der Handhabung von Funktionen findensich in Scala auch viele weitere Besonderheiten funktionaler Sprachen wie List Comprehensions,Lazy Initialization, Pattern Matching, Tail Call Optimization oder Currying. Außerdem bietet Sca-la eine breite Unterstützung für die Generierung von seiteneffektfreiem Code und die Wahrungreferenzieller Transparenz. Dazu enthält die Standard-Bibliothek beispielsweise neben den verän-derbaren (mutable) eine große Anzahl an unveränderbaren (immutable) Datenstrukturen (wiez. B. die Datentypen List, Set und Map).

Letztlich ist es dem Nutzer in den meisten Fällen selbst überlassen mit welchem Paradigma erein Problem lösen will. Grundsätzlich können Scala-Programme wie Java-Programme auch voll-kommen imperativ aufgebaut werden (bis auf syntaktische Unterschiede und wenige Änderun-gen an objektorientierten Konzepten kann in Scala genauso wie in Java programmiert werden),zusätzlich bietet sich aber auch häufig die Möglichkeit imperative Konstrukte zu vermeiden undfunktionale Lösungsansätze einzubringen oder beide Programmierparadigmen zu kombinieren.

1.2 Geschichte und Entwicklung

Die Programmiersprache Scala wird seit 2001 am Labor für Programmiermethoden an der Écolepolytechnique fédérale de Lausanne (EPFL), einer technisch-naturwissenschaftlichen Universi-tät in der Schweiz, von einer Gruppe von Wissenschaftlern unter der Leitung von Martin Oderskyentwickelt. Neben dem eigentlichen Sprachentwurf gehört dabei auch die Entwicklung der zu-gehörigen Bibliotheken und des Compilers scalac zu den Aufgaben des Teams.

Für den Erfinder der Programmiersprache, Martin Odersky, war die Entwicklung von Scala nichtder erste Versuch funktionale und objektorientierte Programmieraspekte zu verknüpfen. Nach-dem er um 1988 während des Abschlusses seiner Promotion seine Begeisterung für die funktio-nale Programmierung entdeckte und bald darauf auf die objektorientierte ProgrammierspracheJava (damals noch in der Alpha-Version) aufmerksam wurde, entschied Martin Odersky sich,mit seinem damaligen Forschungspartner eine funktionale Sprache auf Basis der Java VirtualMachine zu schreiben. Daraus entstand die Programmiersprache Pizza, welche eine Obermengevon Java darstellte und drei Aspekte der funktionalen Programmierung realisierte: GenerischeProgrammierung, Funktionen höherer Ordnung und Pattern Matching. Pizza wurde 1996 veröf-fentlicht, war aber selbst nach Oderskys Ansicht mäßig erfolgreich darin, die Implementierungeiner funktionalen Sprache in der Java-Laufzeitumgebung zu demonstrieren [OV09].Aus den Arbeiten an der generischen Programmierung und in Kollaboration mit Sun Microsys-tems entstand um 1997 GJ (Generic Java), welches sechs Jahre später mit Java 5 offiziell in denSprachumfang eingebunden wurde. Zuvor wurde mit der Veröffentlichung von Java 3 bereitsder von Odersky geschriebene GJ-Compiler als neuer Standard-Compiler javac übernommen.

Lasse Kristopher Meyer

Page 6: Die multiparadigmatische Programmiersprache Scalamh/lehre/bachelor_seminar...1In Scala können Methoden in Operatorenschreibweise definiert werden. Eine äquivalente Schreibweise

1 Einordnung 3

Nachdem Odersky 1999 eine Professur an der EPFL antrat sah er die Gelegenheit seine neuge-wonnene akademische Freiheit zu nutzen, um sich der Entwicklung einer neuen Programmier-sprache zu widmen, ohne sich den Einschränkungen Javas unterwerfen zu müssen [Ode06].War es vorher noch Ziel seiner Arbeit gewesen die Sprache Java zu verbessern und zu erwei-tern, so wollte Odersky nun eine bessere Sprache entwerfen, die sich nur noch der InfrastrukturJavas (JVM und Bibliotheken) bediente, aber nicht mehr auf der Sprache selbst basierte. Ausdieser Idee enstand Funnel, eine Programmiersprache mit minimalem Kern, die auf einem Pro-zesskalkül für Programmiersprachen beruhte, aber weiterhin funktionale mit objektorientertenAspekten verknüpfte. Funnel erwies sich allerdings als zu akademisch und unpraktikabel für denallgemeinen Gebrauch, nicht zuletzt wegen der geringen Unterstützung durch Standard-Biblio-theken. Infolgedessen konzentrierten sich Odersky und seine Mitarbeiter darauf, eine multipara-digmatische Programmiersprache zu entwerfen, die einen Mittelweg zwischen dem minimalis-tischen Funnel und dem eher pragmatischen GJ einschlagen sollte und als vollwertige Sprachefür reale Anwendungen konzipiert war. 2002 verlieh man dieser Sprache den Namen Scala.

2003 wurde Scala in der Version 1.0 für die Java-Plattform veröf-Datum Version08.12.2003 1.0.012.03.2006 2.0.011.10.2006 2.2.021.03.2007 2.4.011.09.2007 2.6.014.07.2010 2.8.004.01.2013 2.10.0

Tabelle 1.1: Versionenvon Scala

fentlicht, 2004 folgte eine Implementierung für .NET. Seitdem wirdScala kontinuierlich weiterentwickelt. Seit Anfang 2011 ist dieser Zu-stand durch eine finanzielle Förderung von 2,3 Millionen Euro überfünf Jahre vom European Research Council langfristig gewährleistet.Durch die Förderung soll vor allem die Forschung im Bereich der par-allelen Programmierung vorangetrieben werden. Im Mai 2011 grün-dete Martin Odersky mit seinen Mitarbeitern zudem das Unterneh-men Typesafe, Inc., welches sich dem kommerziellen Support Scalasund verwandter Softwarekomponenten wie zum Beispiel dem PlayFramework (s. Abschnitt 2.2.2) widmet. Abbildung 1.2 zeigt einen

kleinen Auszug aus der Versionsgeschichte von Scala. Bei vielen Versionsveröffentlichungen er-gab sich die Problematik, dass der Sprachumfang wegen grundlegender Änderungen nicht mehrrückwärtskompatibel war. Deshalb waren viele Nutzer der Sprache gezwungen Teile ihres Codesumzuschreiben. Seit Version 2.0 ist auch der Compiler scalac selbst komplett in Scala geschrie-ben. Derzeit befindet sich Scala in der am 04.01.2013 veröffentlichten Version 2.10.0.

1.3 Einflüsse anderer Programmiersprachen

Die Entwicklung Scalas wurde durch viele andere Programmiersprachen nachhaltig beeinflusst.Tatsächlich sind viele der Funktionen. die Scala bietet, schon aus anderen Programmiersprachenbekannt, wenngleich Scala auch neue Ansätze für bekannte Konzepte anbietet (z. B. die flexibleHandhabung von Traits).Tabelle 1.2 enthält eine kleine Übersicht über die Einflüsse anderer Programmiersprachen aufeinige Sprachbestandteile in Scala. Einige der hier aufgeführten Bestandteile werden zudem mitBeispielen in Kapitel 3 näher beschrieben.

Lasse Kristopher Meyer

Page 7: Die multiparadigmatische Programmiersprache Scalamh/lehre/bachelor_seminar...1In Scala können Methoden in Operatorenschreibweise definiert werden. Eine äquivalente Schreibweise

1 Einordnung 4

Bestandteil Einflüsse BeschreibungSyntax Java, C# Die Syntax der Ausdrücke, Zuweisungen, Klassen, Pakete und

Imports enspricht im Wesentlichen der Java-Syntax.Funktionaleund objektori-ent. Prog.

Ruby, Smalltalk,Python

Verknüpfung funktionaler und objektorientierter Programmie-rung.

Klassenbi-bliotheken,Basistypen

Java Scala ist vollständig kompatibel zu Javas Bibliotheken und im-plementiert viele selbst. Beispielsweise werden Javas Basisty-pen durch eigene Klassen realisiert.

Uniform objectmodel

Smalltalk In Scala werden alle Daten als Objekte betrachtet. Im Gegen-satz zu Java gibt es z. B. keinen Unterschied zwischen Objek-ten und primitiven Datentypen.

Universal nes-ting

Algol, Simula Fast alle Konstrukte in Scala können beliebig tief geschachteltwerden (Klassen in Klassen, Funktionen in Funktionen, ...).

Uniform accessprinciple

Eiffel Der Zugriff auf Bauteile einer Klasse (Attribute, Methoden)kann einheitlich erfolgen wenn keine Parameter übergebenwerden müssen. So bleibt verborgen ob der Inhalt einer Va-riable ausgelesen oder eine Methode aufgerufen wird.

FunktionaleProgrammier-konzepte

ML, OCaml,Haskell

Typische Funktionen höherer Ordnung sind bspw. in ScalasStandard-Bibliothek enthalten (z. B. die Methoden map, foldlund foldr).

Actor model Erlang Programmiermodell für Nebenläufigkeit. Aktoren sind neben-läufige Einheiten mit getrenntem Speicherbereich, die Nach-richten an andere Aktoren versenden, Nachrichten erhaltenund diese verarbeiten können.

Tabelle 1.2: Einflüsse anderer Programmiersprachen auf den Entwurf Scalas

Diese Auflistung entält nur eine Teilmenge der Bestandteile, die in Scala integriert wurden.Die genannten Sprachen gelten allerdings als die wichtigsten Einflüsse auf den Sprachentwurf[OSV10].

Lasse Kristopher Meyer

Page 8: Die multiparadigmatische Programmiersprache Scalamh/lehre/bachelor_seminar...1In Scala können Methoden in Operatorenschreibweise definiert werden. Eine äquivalente Schreibweise

2 Anwendungsgebiete 5

2 Anwendungsgebiete

Im zweiten Abschnitt soll vorerst erläutert werden mit welcher Intention die Programmierspra-che Scala entwickelt wurde, welche Einsatzgebiete beabsichtigt waren und welche Eigenschaf-ten zu diesem Zweck in Scala integriert wurden. Anschließend wird an zwei Beispielen verdeut-licht, wo Scala bereits eingesetzt wird.

2.1 Wofür ist Scala beabsichtigt?

Auf der Internetpräsenz von Typesafe wird Scala als eine „general purpose programming lan-guage“ [Typ] bezeichnet deren Entwicklung folgende Ziele hatte:

• Die Umsetzung bekannter Programmiermuster soll in prägnanter, eleganter Form mög-lich sein. Üblicherweise wird erwartet, dass Scala die Menge an Code (z. B. gegenüberJava) halbiert, vor allem aufgrund des hohen Abstraktionsniveaus funktionaler Program-mierung, die dazu tendiert wesentlich weniger Code zu benötigen.

• Typsicherheit durch statische Typisierung mit dem Ziel viele Arten von Fehlern bereitszur Kompilierzeit zu erkennen. Gleichzeitig unterstützt Scala Typinferenz um redundanteTypangaben innerhalb des Codes auf ein Minimum zu reduzieren (s. Abschnitt 3.1.1).

• Verbindung objektorientierter und funktionaler Programmierung.

• Vollständige Interoperabilität mit Java und mit Javas Infrastruktur (s. Abschnitt 4.1), vorallem damit Scala ohne Schwierigkeiten in schon bestehende Java-Projekte eingebundenwerden kann.

• Unkomplizierte Unterstützung nebenläufiger Programmierung und Nutzung der Möglich-keiten moderner Mehrkern-Architekturen, insbesondere durch die Eigenschaften funktio-naler Programmierung. Konstante Werte, referentielle Transparenz und eigene Speicherbe-reiche für nebenläufige Einheiten sollen den Code vorhersehbarer und somit verlässlichermachen.

• Skalierbarkeit. Scala ist ebenso für kleine Anwendungen wie auch für kommerzielle Pro-jekte mit großen Entwicklerteams konzipiert. Zur Unterstützung kommerzieller Projektestellt Typesafe beispielsweise die Software-Plattform Typesafe Stack zur Verfügung (s. Ab-schnitt 4.3).

• Erweiterbarkeit der Sprache. Durch eigene Bibliotheken hinzugefügte Sprachkomponen-ten sollen sich dabei wie native Sprachunterstützung anfühlen. Dadurch soll es möglichsein, domänenspezifische Sprachen (DSL) zu erstellen, damit Scala auch in Bereichen ein-gesetzt werden kann, in denen keine Programmierer mit umfangreichem Zusatzwissenvorhanden sind.

Lasse Kristopher Meyer

Page 9: Die multiparadigmatische Programmiersprache Scalamh/lehre/bachelor_seminar...1In Scala können Methoden in Operatorenschreibweise definiert werden. Eine äquivalente Schreibweise

2 Anwendungsgebiete 6

2.2 Wo wird Scala bereits eingesetzt?

Trotz der eher langsam (aber kontinuierlich) zunehmenden Popularität der Sprache unter Ent-wicklern [Red12] setzen bereits viele Unternehmen bei der Entwicklung ihrer kommerziellenSoftware und Produktionssysteme auf Scala. Zu Typesafes Kunden zählen beispielsweise Linked-In, Twitter, The Guardian, Sony und Siemens [Typ12]. Die Einsatzgebiete Scalas reichen dabeivon der Programmierung asynchroner, nebenläufiger Softwarekomponenten bis hin zur Ent-wicklung komplexer Bibliotheken zur Aktualisierung und Verwaltung von Datenbankschemata.Weiterhin ist Scala Bestandteil einiger Web Application Frameworks wie dem Play Frameworkund dem Lift Framework. Anhand der Beispiele Twitter und dem Play Framework soll im Folgen-den erläutert werden, wo und warum Scala in kommerziellen Anwendungen oder Open-Source-Projekten bereits Verwendung findet.

2.2.1 Beispiel 1: Twitter

Der Mikrobloggingdienst Twitter war ursprünglich eine reine Ruby on Rails-Applikation. Heutespielt Rails vor allem im Front-End-Bereich die größte Rolle, während im Back-End-Bereich seit2008 zunehmend Programmiersprachen verwendet werden, die zur Java Virtual Machine kom-patibel sind. Scala kommt seitdem primär bei der Bereitstellung langlebiger Serverprozesse aufder JVM zum Einsatz. Weiterhin wurde das in Ruby geschriebene Message-Queueing-System,welches beispielsweise Nutzeraktionen in Warteschlangen einreiht und an Back-End-Daemonszur Bearbeitung weiterleitet, komplett in Scala umgeschrieben. Als Gründe für den Umstieg vonRuby auf Scala nennen die Entwickler einerseits die Flexibilität des Typsystems, das im Gegen-satz zu Rubys Typsystem zwar statisch ist, durch Typinferenz aber nicht immer strenge Typ-angaben verlangt, andererseits ermöglichte Scala auch einen besseren Zugang zur Umsetzungasynchroner Prozesse aufgrund der Kompatibilität zur JVM [JPPV09].

2.2.2 Beispiel 2: Play Framework

Das Play Framework ist ein in Java und Scala geschriebenes, quelloffenes Web Application Fra-mework und Teil des Typesafe Stacks. Seit Version 1.1 unterstützt das Play Framework nebenJava die Programmiersprache Scala und seit Version 2.0 (die zusammen mit dem Typesafe Stack2.0 veröffentlicht wurde) ist das Framework selbst komplett in Scala geschrieben. Der Umstiegauf Scala war laut den Entwicklern die konsequente Anpassung an die steigenden Ansprüchean Webanwendungen. Scalas Actor Model (bzw. dessen Implementierung mit der Middlewa-re Akka, ebenfalls Bestandteil des Typesafe Stacks) bildet beispielsweise die Grundlage für dieVerwaltung nebenläufiger, persistenter Serververbindungen und die Entwicklung verteilter Sys-teme. Das ursprünglich Groovy-basierte und somit dynamisch getypte Template-System des PlayFrameworks wurde zugunsten der frühzeitigen Fehlererkennung und Fehlerbehandlung durchstatische Typisierung ebenfalls mit Scala umgesetzt [Pla11].

Lasse Kristopher Meyer

Page 10: Die multiparadigmatische Programmiersprache Scalamh/lehre/bachelor_seminar...1In Scala können Methoden in Operatorenschreibweise definiert werden. Eine äquivalente Schreibweise

3 Ausgewählte Konzepte 7

3 Ausgewählte Konzepte

In Kapitel 3 werden anhand von Beispielen zu den wichtigsten allgemeinen, objektorientiertenund funktionalen Konzepten die Syntax und die Struktur von Scala erläutert.

Scalas Syntax ist stark durch Syntaxkonventionen aus Java und C# beeinflusst. Im Folgendenwerden nur die wichtigsten Abweichungen und Ergänzungen behandelt. Grundsätzlich wird inKapitel 3 Basiswissen über Syntax und Strukturen der Programmiersprache Java vorausgesetzt.

3.1 Allgemeine Konzepte

3.1.1 Variablen und Funktionen

Scala unterscheidet zwei Arten von Variablen, vals (values) und vars (variables). Dabei entspre-chen vals Javas final-Variablen, es kann ihnen also nur ein einziges Mal ein Wert zugewiesenwerden. vars hingegen kann jederzeit ein neuer Wert zugewiesen werden. Folgendes Beispieldemonstriert die Definition zweier Variablen:

val n: Int = 42var s: String = "Hello, world!"

Das Beispiel zeigt zwei Unterschiede zu Java: Erstens erfolgt die Typangabe einer Variable in Sca-la nach dem Schema Variable: Typ (statt Typ Variable) und zweitens müssen Statementsin Scala nicht zwingend mit einem Semikolon abgeschlossen werden. In den meisten Fällen be-handelt der Compiler stattdessen ein Zeilenende wie ein Semikolon (Semicolon Inference).Die Unterscheidung in vals und vars resultiert aus der Verknüpfung objektorientierter (impera-tiver) und funktionaler Programmierung. vals sind dabei häufig eher im funktionalen Kontextals Bindung eines Namens an einen Ausdruck zu verstehen (vgl. define in Racket).

Die Definition einer Funktion wird in Scala mit dem Schlüsselwort def eingeleitet:

def greet(): Unit = { println("Hello, world!") }def add(x: Int, y: Int): Int = { x + y }

Der Rückgabetyp Unit der Funktion greet ist vergleichbar mit Javas void-Typ und kennzeich-net eine Funktion, die nur ihrer Seiteneffekte wegen aufgerufen wird. Per Konvention werdenRückgabetyp und das Gleichheitszeichen bei solchen Funktionen weggelassen, um das Ausseheneiner Prozedur zu erreichen. Die Funktion add hingegen liefert ein Ergebnis vom Typ Int zurück.Enthält der Rumpf einer Funktion mit einem Rückgabewert1, der nicht vom Typ Unit ist, nur eineinziges Statement, so sind die geschweiften Klammern optional. Die beiden Funktionen ließensich ein wenig prägnanter also auch wie folgt definieren:

def greet() { println("Hello, world!") }def add(x: Int, y: Int): Int = x + y

1Ohne die Angabe des Schlüsselworts return ist der Wert des letzten Ausdrucks im Rumpf der Rückgabewert.

Lasse Kristopher Meyer

Page 11: Die multiparadigmatische Programmiersprache Scalamh/lehre/bachelor_seminar...1In Scala können Methoden in Operatorenschreibweise definiert werden. Eine äquivalente Schreibweise

3 Ausgewählte Konzepte 8

Sowohl für Variablen als auch für Funktionen in Scala gilt: Redundante (doppelte) oder herleit-bare Typangaben können oft vermieden werden:

val b = true // instead of ’val b: Boolean = true’var l = List("a", "b") // instead of ’var l: List[String] = List("a", "b")’def square(x: Double) = x * x // instead of ’def square(x: Double): Double = x * x’

In diesen Fällen kann der Compiler aus dem Kontext rückschließen, welche Typangaben er beimÜbersetzen ergänzen muss. Dieser Mechanismus wird als Typinferenz bezeichnet und sorgt z. B.dafür, dass das Programmieren in Scala deutlich weniger Schreibarbeit erfordert als in Java.

3.1.2 for-Ausdrücke

Die meisten Kontrollstrukturen (if-Abfragen, while-Schleifen, ...) in Scala sind syntaktisch undsemantisch ähnlich zu benutzen wie in Java. Eine Ausnahme bilden for-Ausdrücke, welche imGegensatz zu anderen imperativen Programmiersprachen die Iteration über mehrere Collecti-ons, das Filtern von Werten und das Produzieren neuer Collections erlauben. Listing 3.1 zeigtein Scala-Programm, das alle Kommandozeilenparameter mit mehr als zwei Zeichen in die Stan-dardausgabe schreibt.

1 object ForExpressions {2 def main(args: Array[String]) {3 for (4 arg <- args // generator5 if (arg.length > 2) // filter6 ) println(arg) // body (single statement, curly braces optional)7 }8 }

Listing 3.1: Filtern mit for-Ausdrücken

Die Iteration in for-Ausdrücken erfolgt immer über mindestens einen Generator „<-“ (Zeile 4).Dieser bindet den zu betrachtenden Wert an die val-Variable arg. Filter werden über beliebigviele if-Klauseln realisiert, die sich dem Generator anschließen (Zeile 5).Wie der Name schon andeutet können for-Ausdrücke auch Werte zurückliefern. In Listing 3.2werden alle Dateien im aktuellen Arbeitsverzeichnis in einem Array abgelegt. Danach werdenalle Scala-Dateien mit einem for-Ausdruck in einem neuen Array zusammengefasst.

1 val files = (new java.io.File(".")).listFiles23 val scalaFiles =4 for (5 file <- files6 if file.getName.endsWith(".scala")7 ) yield file // yield goes before the entire body

Listing 3.2: Rückgabewerte von for-Ausdrücken

Das Schlüsselwort yield legt in jeder Iteration den Wert des Rumpfes (hier nur file) in einerneuen Collection ab. Der Typ der neuen Collection hängt einerseits vom Typ der Collection abüber die iteriert wird und andererseits vom Typ des Wertes aus dem Rumpf. In diesem Fall habensowohl files als auch scalaFiles den Typ Array[java.io.File]2.Durch das Hinzufügen weiterer Generatoren im Kopf des for-Ausdrucks lassen sich im Übrigen

2Java-Klassen lassen sich auf gewohnte Weise in Scala-Programmen verwenden (s. Abschnitt 4.1).

Lasse Kristopher Meyer

Page 12: Die multiparadigmatische Programmiersprache Scalamh/lehre/bachelor_seminar...1In Scala können Methoden in Operatorenschreibweise definiert werden. Eine äquivalente Schreibweise

3 Ausgewählte Konzepte 9

geschachtelte Iterationen erreichen. Außerdem dürfen zwischen den Generatoren und FilternVariablen definiert werden, auf die man auch noch im Rumpf des for-Ausdrucks zugreifen kann.

3.2 Objektorientierte Konzepte

3.2.1 Grundlagen, Klassen und Vererbung

Üblicherweise sind Scala-Programme in Bibliotheken strukturiert, die wiederum in Pakete un-terteilt sind, welche Klassen, Singleton-Objekte und Traits enthalten. Wie in Java können Kom-ponenten einer solchen Bibliothek über das Schlüsselwort import in den eigenen Quelltext ein-gebunden und benutzt werden. Im Gegensatz zu Java können allerdings mehrere dieser Kompo-nenten in einer Datei mit frei wählbarem Namen (und der Endung „.scala“) definiert werden.

Klassendefinitionen werden mit dem Schlüsselwort class eingeleitet. Jede Klasse hat genaueinen Primärkonstruktor, dessen Argumente (auch Klassenparameter genannt) bei der Definitiondirekt nach dem Klassennamen angegeben werden:

class Complex(val real: Double, val imag: Double)

Wird kein expliziter Zugriffmodifikator angegeben, so sind Klassen, Attribute und Methoden inScala implizit public. Andernfalls sind nur die Modifikatoren protected3 und private erlaubt.Kennzeichnet man außerdem die Klassenparameter mit dem Variablentyp (und Modifikatoren),werden vom Compiler automatisch Attribute mit den entsprechenden Zugriffsrechten angelegt.Um mit den Objekten der Klasse Complex nun auch rechnen zu können, ergänzen wir den bisherleeren Rumpf (leere geschweifte Klammern sind optional) um ein Attribut und einige Methoden:

1 import scala.math._

23 class Complex(val real: Double, val imag: Double) {4 println("Created: " + real + " + " + imag + "i")56 val abs = sqrt((real * real) + (imag * imag))78 def +(d: Double) =9 new Complex(this.real + d, this.imag)

10 def +(that: Complex) =11 new Complex(this.real + that.real, this.imag + that.imag)12 override def toString = real + " + " + imag + "i"13 }

Listing 3.3: Complex.scala (1. Teil)

Der Scala-Compiler interpretiert jeden Code, der nicht innerhalb einer Attributs- oder Metho-dendefinition steht, als Bestandteil des Primärkonstruktors. Der Code in Zeile 4 wird also beijeder Instanziierung ausgeführt. Dies zeigt auch die Ausgabe des Interpreters (s. Abschnitt 4.2):

scala> val z = new Complex(1.0, 1.0)Created: 1.0 + 1.0iz: Complex = 1.0 + 1.0i

Auf die Definition von Zugriffsfunktionen (Getter/Setter) für die Attribute real, imag und absin Zeile 6 kann verzichtet werden, da durch die Deklaration als (public) val sichergestellt ist,

3Im Gegensatz zu Java beschränkt Scalas protected die Sichtbarkeit allerdings auf abgeleitete Klassen.

Lasse Kristopher Meyer

Page 13: Die multiparadigmatische Programmiersprache Scalamh/lehre/bachelor_seminar...1In Scala können Methoden in Operatorenschreibweise definiert werden. Eine äquivalente Schreibweise

3 Ausgewählte Konzepte 10

dass die Werte zwar abrufbar, aber nicht veränderbar sind. In Scala werden Objekte, deren Zu-stand sich nicht ändern kann, als funktionale Objekte bezeichnet.In den Zeilen 8 und 10 wird nun anschließend die Methode + in Operatorenschreibweise de-finiert und überladen, um die Addition mit reellen und komplexen Zahlen zu implementieren.Tatsächlich gewinnen wir durch diese Überladung sogar mehr. Beispielsweise lassen sich unserekomplexen Zahlen nun auch ohne Probleme mit ganzen Zahlen (vom Typ Int) addieren:

scala> z + 1Created: 2.0 + 1.0ires0: Complex = 2.0 + 1.0i

Diese Berechnung funktioniert, weil Scala eine implizite Konvertierung des Typs Int zum TypDouble vornimmt. Eine Methodenüberladung reicht also aus, um unsere komplexen Zahlen mitsich selbst und allen anderen numerischen Typen (Byte, Short, Int, ...) addieren zu können.4

Die Methode toString überschreibt schließlich die gleichnamige Methode in der Klasse Any, derWurzel der Scala-Klassenhierarchie, von der jede Klasse direkt oder indirekt erbt. Der override-Modifikator muss beim Überschreiben einer konkreten Methode zwingend gesetzt werden, damitÜberschreibungsfehler vom Compiler erkannt werden können.

Vererbung wird wie in Java mit dem Schlüsselwort extends gekennzeichnet. Ohne Angabe derextends-Klausel ergänzt der Compiler implizit die Klasse AnyRef als Superklasse. AnyRef istScalas Pendant zu Javas java.lang.Object und neben AnyVal (der Superklasse der WerttypenByte, Short, Int, ...) die einzige direkte Subklasse von Any.In Listing 3.4 leiten wir die Klasse Programmer von der abstrakten Klasse Employee ab. Klassen,die abstract deklariert sind, dürfen abstrakte Attribut- oder Methodendeklarationen enthalten.Falls abstrakte Deklarationen vorhanden sind, müssen diese allerdings in abgeleiteten, konkretenKlassen implementiert werden, ansonsten würde der Compiler eine Fehlermeldung ausgeben.

1 abstract class Employee(val name: String) {2 def work() // abstract method3 override def toString = name // concrete method4 }56 class Programmer(name: String) extends Employee(name) {7 def work() { println(name + " programs some software.") } // concrete method8 }

Listing 3.4: Abstrakte Klassen und Vererbung

Die Klasse Programmer erbt von Employee also das Attribut name und die konkrete MethodetoString (insbesondere weil diese Elemente public sind) und implementiert die abstrakte Me-thode work.Bei der Instanziierung einer Klasse müssen auch immer die Attribute aller Superklassen initia-lisiert werden. Da Konstruktoren in Scala nicht mitvererbt werden, muss deshalb immer einexpliziter Konstruktor der Superklasse angegeben werden, selbst wenn diese abstrakt ist. Andiesen Konstruktor wird in Zeile 6 der Klassenparameter name weitergereicht. Würden wir nameebenfalls als val kennzeichnen, so würde der Compiler ein neues Attribut name in Programmeranlegen. Da das Attribut in der Klasse Employee aber konkret ist (es wird ja auf jeden Fall in-itialisiert) würden wir dieses damit überschreiben. Diese Überschreibung müsste dann explizitgekennzeichnet werden:

4Dabei ist allerdings die Reihenfolge der Operanden wichtig. Im umgekehrten Fall 1 + z wäre die Definition einerneuen impliziten Konvertierung vom Typ Int zum Typ Complex notwendig.

Lasse Kristopher Meyer

Page 14: Die multiparadigmatische Programmiersprache Scalamh/lehre/bachelor_seminar...1In Scala können Methoden in Operatorenschreibweise definiert werden. Eine äquivalente Schreibweise

3 Ausgewählte Konzepte 11

class Programmer(override val name: String) extends Employee(name) { ... }

Ohne die Kennzeichnung eines Klassenparameters als val oder var werden allerdings keineAttribute angelegt. Der Parameter wird hier also einfach an den Konstruktor weitergereicht.

Das Konkretisieren neuer Subklassen (von abstrakten Klassen) enspricht dem Prinzip des Poly-morphismus (subtyping polymorphism). Wie erwartet können wir die Subklassen der abstraktenKlasse Employee wie gewohnt instanziieren und benutzen:

scala> val steve = new Programmer("Steve")steve: Programmer = Steve

scala> steve.nameres0: String = Steve

scala> steve.work()Steve programs some software.

3.2.2 Singleton-Objekte

Da Scala rein objektorientiert ist können Klassen keine statischen Attribute oder Methoden ha-ben. Stattdessen lassen sich in Scala mit dem Schlüsselwort object Singleton-Objekte definie-ren, auf deren Attribute und Methoden über die Java-übliche Notation für statics zugegriffenwerden kann (z. B. Thread.yield()). Werden ein Singleton-Objekt und eine Klasse mit glei-chem Namen in derselben Quelldatei definiert, so bezeichnet man das Objekt als companionobject der Klasse und die Klasse als companion class des Singleton-Objekts:

1 object Complex {2 def apply(real: Double, imag: Double) = new Complex(real, imag) // factory method3 }

Listing 3.5: Complex.scala (2. Teil)

Listing 3.5 ergänzt die Datei Complex.scala aus Listing 3.3 um ein companion object mit derMethode apply. Diese Methode wird als factory method bezeichnet, da sie bei der Instanziierungeiner Klasse ohne das Schlüsselwort new implizit aufgerufen wird:

val z = Complex(4.0, 2.0) // calls Complex.apply(...)

Eine Klasse und ihr companion object haben gegenseitigen Zugriff auf private Attribute und Me-thoden. Auf diese Weise können wie in Java statische Bestandteile einer Klasse erzeugt werden.Wie das Schlüsselwort object bereits andeutet, sind Singleton-Objekte selbst auch First-Class-Objekte. Sie können beispielsweise andere Klassen erweitern, Methoden erben und überall dorteingesetzt werden, wo der Typ der Superklasse erwartet wird:

1 abstract class Planet(val name: String) {2 def collideWith(that: Planet) {3 println("Oh no! The " + this.name + " collides with the " + that.name + "!")4 }5 }67 object Earth extends Planet("Earth")8 object Sun extends Planet("Sun")

Listing 3.6: Singleton-Objekte und Vererbung

Lasse Kristopher Meyer

Page 15: Die multiparadigmatische Programmiersprache Scalamh/lehre/bachelor_seminar...1In Scala können Methoden in Operatorenschreibweise definiert werden. Eine äquivalente Schreibweise

3 Ausgewählte Konzepte 12

Die Singleton-Objekte aus Listing 3.6 können wie gewöhnliche Objekte benutzt werden:

scala> Earth.collideWith(Sun)Oh no! The Earth collides with the Sun!

Singleton-Objekte ohne companion class werden als standalone object bezeichnet. Im Gegensatzzu Klassen definieren standalone objects keinen neuen Typ. Ein Beispiel für Singleton-Objekteohne companion class haben wir bereits in Listing 3.1 gesehen. Die Hauptmethode main wirdüblicherweise als Einstiegspunkt in einem standalone object definiert.

3.2.3 Traits

Traits (Eigenschaften, Charakteristiken) sind wiederverwendbare Sammlungen von Attribut-und Methodendefinitionen, welche Klassen und Objekten beigemischt werden können. Traitssind grob vergleichbar mit Javas Interfaces (einer Klasse können mehrere Traits beigemischtwerden, Traits definieren einen Typ, ...), mit dem Unterschied, dass in Traits, genau wie in Klas-sen, auch konkrete Attribute und Methoden definiert werden dürfen. Die Definition eines Traitswird mit dem Schlüsselwort trait eingeleitet, das Beimischen erfolgt über extends (bei Klassenohne explizite Superklasse) oder with (bei Klassen mit expliziter Superklasse oder Objekten):

1 trait ScalaSkills extends Employee {2 val areaOfExpertise: String // abstract field3 def skills() { println(this + " likes programming in Scala.") } // concrete method4 }56 class ScalaProgrammer(name: String, val areaOfExpertise: String)7 extends Programmer(name) with ScalaSkills

Listing 3.7: Abstrakte und konkrete Elemente in Traits

Erweitert ein Trait eine explizite Klasse (hier Employee aus Listing 3.4), so kann es nur in de-ren Subklassen (-objekte) gemischt werden. Das Beimischen eines Traits ist semantisch gleich-bedeutend mit dem Erben von einer abstrakten Klasse: Konkrete Elemente werden vererbt(oder überschrieben) und abstrakte Elemente müssen implementiert werden. In Listing 3.7 erbtScalaProgrammer bspw. skills und implementiert areaOfExpertise (wegen val in Zeile 6).Die Verwendung konkreter Methoden erlaubt das Definieren umfangreicherer Schnittstellen, diedie redundanten Implementierungen identischer Methoden in mehreren Klassen des Traits ver-meiden. Neben der Möglichkeit, einer Klasse mehrere Traits beizumischen, gibt es einen weite-ren wichtigen Unterschied zur Vererbung: super-Aufrufe sind nicht statisch, sondern dynamischgebunden. Dieser Umstand ermöglicht sogenannte stapelbare Modifikationen für Objekte.

1 trait Motivation extends Employee {2 abstract override def work() { println(this + " is motivated!"); super.work() }3 }4 trait Laziness extends Employee {5 abstract override def work() { println(this + " is lazy..."); super.work() }6 }

Listing 3.8: Stapelbare Modifikationen

In Listing 3.8 definieren wir zwei Traits, die verschiedene Modifikationen für die abstrakte Me-thode work zur Verfügung stellen. Der entscheidende Punkt ist, dass der super-Aufruf vomCompiler nicht statisch gebunden wird. Stattdessen wird die aufzurufende Implementierung vonwork bei jedem Beimischen in eine konkrete Klasse zur Laufzeit neu ermittelt. Der super-Aufruf

Lasse Kristopher Meyer

Page 16: Die multiparadigmatische Programmiersprache Scalamh/lehre/bachelor_seminar...1In Scala können Methoden in Operatorenschreibweise definiert werden. Eine äquivalente Schreibweise

3 Ausgewählte Konzepte 13

funktioniert deshalb nur, wenn das Trait nach einem Trait oder einer Klasse mit der konkretenImplementierung der Methode work beigemischt wird. Diesen Sachverhalt teilt man dem Com-piler über die Modifikatorenkombination abstract override mit, die nur bei Traits erlaubt ist.Wir erhalten nun die Möglichkeit (stapelbare) Modifikationen an Objekten und ihren Methodenvorzunehmen, indem wir die Traits in bestimmter Reihenfolge beimischen:

scala> val bill = new Programmer("Bill") with Motivation with Lazinessbill: Programmer with Motivation with Laziness = Bill

scala> bill.work()Bill is lazy...Bill is motivated!Bill programs some software.

Mehrere abstrakte super-Aufrufe werden linearisiert gebunden. Angefangen beim am weitestenrechts beigemischten Trait Laziness wird der Aufruf von work über Motivation bis zur imple-mentierenden Klasse Programmer weitergereicht. Die Linearisierung ist somit auch der Grunddafür, dass die typischen Probleme der Mehrfachvererbung (wie beispielsweise das Diamant-Problem) bei der Verwendung von Traits vermieden werden.

3.3 Funktionale Konzepte

3.3.1 Grundlagen, Funktionen und Closures

Wie wir in Abschnitt 3.2 gesehen haben, können Funktionen, wie in Java, als Methoden inner-halb von Klassen definiert werden. Das Funktionskonzept in Scala ist allerdings allgemeiner.Scalas Funktionen sind First-Class-Objekte. Wie andere Werttypen (Double, Int, ...) dürfenFunktionen in Form von unbenannten Literalen im Quelltext verwendet werden:

val multiply = (x: Double, y: Double) => x * y

Funktionsliterale werden vom Compiler in Klassen umgewandelt, die zur Laufzeit instanziiertwerden. Das erzeugte Objekt bezeichnet man als Funktionswert. Funktionswerte sind gleichbe-rechtigte Objekte, die man, wie im obigen Beispiel, in Variablen ablegen oder als Argumente anandere Funktionen übergeben kann:

scala> List(1, 2, 3).foreach((x: Int) => print(x))123scala> Array(1, 2, 3).filter((x: Int) => x > 1)res0: Array[Int] = Array(2, 3)

Viele Funktionen höherer Ordnung, die aus funktionalen Programmiersprachen wie Haskell be-kannt sind, wurden auch in Scalas Standardbibliothek implementiert. Dynamische Listen wer-den außerdem durch die Klasse List aus dem Paket scala.collection.immutable realisiert:

val l = "a" :: "b" :: "c" :: Nil // is equivalent to ’val l = List("a", "b", "c")’

Hierbei repräsentiert das Singleton-Objekt Nil die leere Liste. Durch target typing und einespezielle Platzhalter-Syntax sind auch kürzere Schreibweisen für Funktionsargumente möglich:

val a = Array(1, 2, 3).filter(x => x > 1) // target typingval b = Array(1, 2, 3).filter(_ > 1) // placeholder syntax

Lasse Kristopher Meyer

Page 17: Die multiparadigmatische Programmiersprache Scalamh/lehre/bachelor_seminar...1In Scala können Methoden in Operatorenschreibweise definiert werden. Eine äquivalente Schreibweise

3 Ausgewählte Konzepte 14

Im ersten Fall erkennt der Compiler, dass x vom Typ Int sein muss, da auf einem Array[Int]operiert wird (target typing). Der Platzhalter _ im zweiten Fall ist immer dann möglich, wennein oder mehrere Parameter des Funktionsliterals in gleicher Reihenfolge im Rumpf der Funktionauftauchen.5 Der Unterstrich kann auch bei einzelnen Argumenten oder kompletten Argument-listen benutzt werden. So erhält man partiell angewandte Funktionen:

def concat(x: String, y: String) = x + yval a = concat _ // a("Hello, ", "World") returns "Hello, world!"val b = concat(_: String, "world!") // b("Hello, ") returns "Hello, world!"

Ein weiteres Einsatzgebiet des Platzhalters _ ist die funktionale Programmiertechnik Currying.Beim Currying werden Funktionen auf mehrere Argumentlisten angewendet, statt nur auf eineeinzige. Auf diese Weise können Funktionen mit mehreren Argumenten auf Funktionen mit einergeringeren Anzahl an Argumemten reduziert werden:

def curriedConcat(x: String)(y: String) = x + y

Durch die Platzhalter-Syntax erhalten wir hier wieder eine partiell angewandte Funktion:

scala> curriedConcat("Hello, ")("world!")res0: String = Hello, world!

scala> val a = curriedConcat("Hello, ") _

a: String => String = <function1>

scala> a("world")res1: String = Hello, world

Funktionsliterale sind nicht nur auf die Verwendung der Funktionsparameter beschränkt. Es istebenso erlaubt sich auf Variablen aus dem Deklarationskontext oder lokale Variablen andererFunktionen zu beziehen. Funktionswerte solcher Funktionsliterale werden Closures genannt:

1 var globalFactor = 2 // globalFactor is reassignable2 val multiply = (x: Int) => x * globalFactor34 def makeMultiplier(localFactor: Int) = (x: Int) => x * localFactor

Listing 3.9: Closures

Aus Sicht der Funktionen in multiply und makeMultiplier sind die Variablen globalFactorund localFactor in Listing 3.9 freie Variablen. Im ersten Fall hängt das Ergebnis des ange-wandten Funktionsliterals vom aktuellen Wert der var-Variable globalFactor ab:

scala> multiply(21)res0: Int = 42

scala> globalFactor = 4globalFactor: Int = 4

scala> multiply(21)res1: Int = 84

Im zweiten Fall referenziert das Closure jeweils eine Kopie der lokalen Variable localFactor:

scala> val multiplyByTwo = makeMultiplier(2)multiplyByTwo: Int => Int = <function1>

5Ist kein target typing möglich, müssen Typen allerdings explizit angegeben werden (z. B.: „(_: Int) > 1“).

Lasse Kristopher Meyer

Page 18: Die multiparadigmatische Programmiersprache Scalamh/lehre/bachelor_seminar...1In Scala können Methoden in Operatorenschreibweise definiert werden. Eine äquivalente Schreibweise

3 Ausgewählte Konzepte 15

scala> multiplyByTwo(21)res3: Int = 42

Eine weitere Eigenschaft Scalas in Bezug auf funktionale Programmierung ist der Umgang desCompilers mit endrekursiven Funktionen (tail recursive functions):

1 def sumOuter(n: Int) = {2 def sumInner(m: Int, n: Int): Int =3 if (n == 0) m4 else sumInner(m + n, n - 1) // tail recursion5 sumInner(0, n)6 }

Listing 3.10: Endrekursive Funktionen

Die Funktion sumOuter in Listing 3.10 berechnet die Summe der ersten n natürlichen Zahlen. Zudiesem Zweck enthält sie die innere, endrekursive Hilfsfunktion sumInner.6 Der Scala-Compilernutzt nun die Tatsache, dass sich jede endrekursive Funktion durch eine Iteration ausdrückenlässt, und optimiert die Funktion sumInner derart, dass beim rekursiven Aufruf kein neuer StackFrame erzeugt wird. Obige Funktion ist also genauso effizient wie eine iterative Lösung.

3.3.2 Case Classes, generische Typen und Pattern Matching

Case Classes und Pattern Matching sind Konstrukte, die das Definieren und Verarbeiten ungekap-selter, meist rekursiver Datenstrukturen erlauben. Case Classes ergänzen herkömmliche Klassenum einige syntaktische Annehmlichkeiten, die insbesondere unkompliziertes Pattern Matchingauf Objekte erlauben. Die Kennzeichnung einer Klasse mit dem Modifikator case ergänzt dieseum:

• eine Fabrikmethode mit dem Namen der Klasse (s. Abschnitt 3.2.2).

• implizite val-Präfixe für alle Klassenparameter, also automatisches Anlegen der Attribute.

• automatische Implementierungen der Methoden toString, hashCode, equals und copy.

Case Classes erlauben beispielsweise die einfache Definition rekursiver algebraischer Datentypen,wie man sie aus funktionalen Programmiersprachen (vgl. z. B. Haskells data) gewohnt ist:

1 abstract class Tree[+T]2 case object Empty extends Tree[Nothing]3 case class Node[T](left: Tree[T], value: T, right: Tree[T]) extends Tree[T]

Listing 3.11: Tree.scala (1. Teil)

Listing 3.11 zeigt eine Möglichkeit Binärbäume in Scala zu implementieren. In Zeile 1 definierenwir dazu zunächst den repräsentativen generischen Typ Tree[T]. Der Typparameter T bestimmtden Typ der Werte, die in den Knoten unseres Binärbaums gespeichert sind. Durch das Präfix+ legen wir außerdem fest, dass für alle Subtypen U von T der Typ Tree[U] ebenfalls ein Sub-typ von Tree[T] ist (Kovarianz; Kontravarianz wird mit - gekennzeichnet). Diese Eigenschaft istnotwendig, da der leere Baum Empty in Zeile 2 vom Typ Tree[Nothing] ist. Die Klasse Nothingist Subklasse aller Klassen T, also kann Empty überall verwendet werden, wo ein Tree[T] er-wartet wird. Knoten realisieren wir mit der Case Class Node[T], für die der Compiler implizitdie Attribute left, value und right anlegt.

6Für rekursive Funktionen muss immer der Typ des Rückgabewerts angegeben werden.

Lasse Kristopher Meyer

Page 19: Die multiparadigmatische Programmiersprache Scalamh/lehre/bachelor_seminar...1In Scala können Methoden in Operatorenschreibweise definiert werden. Eine äquivalente Schreibweise

3 Ausgewählte Konzepte 16

Als nächstes definieren wir mittels Pattern Matching die polymorphe Funktion map[T, U], dieeine Funktion f: T => U auf alle Knoten eines Tree[T] anwendet und einen Tree[U] erzeugt:

1 def map[T, U](f: T => U, t: Tree[T]): Tree[U] = t match {2 case Empty => Empty3 case Node(l, v, r) => Node(map(f, l), f(v), map(f, r))4 }

Listing 3.12: Tree.scala (2. Teil)

Pattern Matching wird über den match-Ausdruck realisiert, der einer Verallgemeinerung desswitch-Statements aus Java entspricht. Währrend switch nur Konstanten in patterns unter-stützt, sind in match auch variable patterns (z. B. l, v und r in Zeile 3), wildcard patterns(dargestellt durch _) und constructor patterns (wie Empty und Node(l, v, r)) erlaubt. Im obi-gen Fall überprüft match die Struktur von t auf das erste passende Pattern und wertet denentsprechenden Ausdruck hinter dem => aus. Wir können map[T, U] wie folgt verwenden:

scala> map((_: Int) + 1, Empty)res0: Tree[Int] = Empty

scala> map((_: Int) > 1, Node(Node(Empty, 1, Empty), 2, Node(Empty, 3, Empty)))res1: Tree[Boolean] = Node(Node(Empty,false,Empty),true,Node(Empty,true,Empty))

Obwohl map[T, U] durch die Typen T und U parametrisiert ist, ist die Angabe dieser Typpa-rameter optional, da der Compiler die entsprechenden Typen durch die Typen der konkretenParameter f und t inferieren kann.

3.3.3 Aktoren und Nebenläufigkeit

Da Scala uneingeschränkt auf alle Java-Bibliotheken zugreifen kann, ist es grundsätzlich möglichNebenläufigkeit mit Javas Threading-Modell oder anderen Konzepten aus der Java-Bibliothekjava.util.concurrent zu realisieren. Um jedoch die Probleme von Nebenläufigkeitsmodel-len mit geteilten Speicherbereichen (Locks, Synchronisation, Zugriffskontrolle auf kritische Berei-che, ...) zu vermeiden, stellt Scala zusätzlich ein eher funktionales Konzept zur nebenläufigenProgrammierung zur Verfügung: das Aktor-Modell. Scalas Aktor-Modell ist eine Implementie-rung des Aktor-Modells der funktionalen Programmiersprache Erlang, in der Aktoren ein festerSprachbestandteil sind. In Scala werden Aktoren in der Bibliothek scala.actors definiert.Aktoren sind Thread-ähnliche Entitäten mit eigenem Speicherbereich, die zusätzlich über eineMailbox verfügen, mit der sie Nachrichten von anderen Aktoren empfangen können. Aktorenkönnen auf zweierlei Arten implementiert werden. Die erste Möglichkeit ist das Erweitern desTraits Actor und die Implementierung der abstrakten Methode act:

1 import scala.actors._

23 object ReceivingActor extends Actor {4 def act() {5 println("I’m acting!")6 while (true) {7 receive { case message => println("Received message: " + message) }8 }9 }

10 }

Listing 3.13: Aktoren implementieren (Das Actor-Trait)

Lasse Kristopher Meyer

Page 20: Die multiparadigmatische Programmiersprache Scalamh/lehre/bachelor_seminar...1In Scala können Methoden in Operatorenschreibweise definiert werden. Eine äquivalente Schreibweise

3 Ausgewählte Konzepte 17

Durch den Aufruf der Methode start wird der Aktor in Form eines neuen Threads gestartet:

scala> ReceivingActor.start()I’m acting!res0: scala.actors.Actor = ReceivingActor$@734ee775

Die zweite Möglichkeit ist das Verwenden der Hilfsmethode actor aus dem Objekt Actor:

1 import scala.actors.Actor._

23 val forwardingActor = actor {4 while (true) {5 receive { case message => ReceivingActor ! message }6 }7 }

Listing 3.14: Aktoren implementieren (Die actor-Methode)

Auf diese Weise wird der Aktor sofort gestartet. Die Methode receive überprüft die Mailboxeines Aktoren auf ungelesene Nachrichten, die einem der angegeben Pattern (case-Fälle) ent-sprechen. Wird eine solche Nachricht gefunden, wird die entsprechende Aktion ausgeführt. An-dernfalls blockiert der Aktor, bis eine passende Nachricht eingeht. Nachrichten werden mit derMethode ! an den aufrufenden Aktoren versendet:

scala> forwardingActor ! "Send this to ReceivingActor!"

scala> Received message: Send this to ReceivingActor!

Wie man sieht leitet der Aktor forwardingActor jede eingehende Nachricht an den AktorReceivingActor weiter, der diese wiederum in die Standardausgabe schreibt. Beide Aktorensind unabhängige, nebenläufige Threads, die nur über Nachrichten kommunizieren können.

Lasse Kristopher Meyer

Page 21: Die multiparadigmatische Programmiersprache Scalamh/lehre/bachelor_seminar...1In Scala können Methoden in Operatorenschreibweise definiert werden. Eine äquivalente Schreibweise

4 Technische Unterstützung 18

4 Technische Unterstützung

Dieses Kapitel beschäftigt sich mit der Kompatibilität zu Java und den technischen Hilfsmittelnzur Programmiersprache Scala wie dem Compiler, dem Interpreter und kompatiblen Entwick-lungsumgebungen. Im letzten Abschnitt wird abschließend der Typesafe Stack erläutert.

4.1 Integration mit Java

Wie bereits in Abschnitt 2.1 erwähnt, war die Interoperabilität mit Java ein wichtiges Ziel bei derEntwicklung Scalas. Deshalb werden Scala-Programme in Java-Bytecode übersetzt und laufenauf der Java Virtual Machine.1 Das hat zur Konsequenz, dass Scala-Programme üblicherwei-se dieselbe Laufzeit-Performance aufweisen wie Java-Programme [OSV10]. Außerdem ist Scalasomit für alle Plattformen verfügbar, für die eine JVM existiert — Scala ist deshalb ebenso platt-formunabhängig wie Java. Die vollständige Kompatibilität mit Java ermöglicht Scala Zugriffauf alle Java-Bibliotheken (Methodenaufrufe, Zugriff auf Attribute, Vererbung und die Imple-mentierung von Interfaces funktionieren wie gewohnt). Umgekehrt kann Scala-Code auch inJava-Programme eingebettet werden und von diesen fast uneingeschränkt benutzt werden.

4.2 Compiler, Interpreter, IDEs

Neben dem eigentlichen Sprachkern enthält die Standard-Scala-Distribution auch den Scala-Compiler scalac, den interaktiven Interpreter scala (s. Abbildung 4.1) und die Standard-Bibliotheken.

Die meistverwendete und empfohlene integrier-

Abbildung 4.1: Der Scala-Interpreter

te Entwicklungsumgebung für Scala ist die Sca-la IDE für Eclipse. Diese bietet die für Java be-kannten Funktionalitäten wie Syntax-Highligh-ting, Autovervollständigung und Abhängigkeits-verwaltung (auch mit Java-Projekten). Nebender Scala IDE für Eclipse gibt es noch Pluginsfür die Entwicklungsumgebungen IntelliJ IDEAund NetBeans.

Außerdem kann Scala in die meisten gängigenEditoren integriert werden. Es sind beispiels-weise Plugins für Emacs, jEdit, Notepad-Plus, TextMate, SubEthaEdit und Textwrangler vor-handen. Manche neuere Editoren wie Sublime Text 2 unterstützen Scala bereits von Haus aus.

1Zusätzlich wurde Scala auch für die .NET-Plattform implementiert, die im Vergleich aber geringeren aktiven Sup-port erfährt.

Lasse Kristopher Meyer

Page 22: Die multiparadigmatische Programmiersprache Scalamh/lehre/bachelor_seminar...1In Scala können Methoden in Operatorenschreibweise definiert werden. Eine äquivalente Schreibweise

4 Technische Unterstützung 19

4.3 Der Typesafe Stack

Der Typesafe Stack ist eine von der Firma Typesafe, Inc. kostenlos zur Verfügung gestellte,quelloffene und plattformunabhängige Software-Plattform, die im Wesentlichen aus den dreiKomponenten Scala (und den zugehörigen Bibliotheken), Akka (ereignisgesteuerte Middlewa-re für die Entwicklung verteilter und nebenläufiger Anwendungen in Scala und Java) und demPlay Framework besteht und als vollständige Entwicklungs- und Verteilungsumgebung für Scala-Entwickler dienen soll. Zu diesem Zweck werden dem Entwickler neben den drei Hauptkompo-nenten viele weitere Tools wie die Scala IDE für Eclipse und das Simple Build Tool (auch sbt;quelloffenes Build Tool für Scala-Projekte, ähnlich Apache Ant) zur Verfügung gestellt. Der Ty-pesafe Stack kann kostenpflichtig (mit der Typesafe Subscription) um kommerziellen Supportbei der Produktion von Anwendungen, professionelle Wartung und zusätzliche Entwicklertoolsergänzt werden. Momentan ist der Typesafe Stack (in der Version 2.0.2) für alle gängigen Be-triebssysteme (Mac OS X, Linux und Windows) erhältlich.

Lasse Kristopher Meyer

Page 23: Die multiparadigmatische Programmiersprache Scalamh/lehre/bachelor_seminar...1In Scala können Methoden in Operatorenschreibweise definiert werden. Eine äquivalente Schreibweise

5 Diskussion und Zusammenfassung 20

5 Diskussion und Zusammenfassung

Als „general purpose programming language“ hatte die Entwicklung Scalas vor allem das Zieleine Sprache zu schaffen, die in vielfältigen Einsatzgebieten Anwendung findet. Die zunehmen-de Verwendung Scalas in verschiedensten (auch industriellen) Bereichen [TIO13] scheint dieserErwartungshaltung gerecht zu werden. Tatsächlich bietet Scala eine endlos erscheinende Men-ge an Funktionen für fast alle erdenklichen Anwendungsfälle. Daraus resultiert allerdings aucheine anfangs schwer überschaubare Sprachkomplexität, die gerade beim Einstieg abschreckendwirkt und Scala seit Beginn der Entwicklung in Verruf bringt, eine eher „akademische Sprache“[The10] zu sein. Im Vergleich zu Java-Programmen erfordert das Lesen und Verstehen von Scala-Programmen beispielsweise mehr Hintergrundwissen über Methodensignaturen, Funktionalitätvon Operatoren oder Typen. Methoden in Operatorenschreibweise und Typinferenz machen denCode zwar prägnanter und kürzer als vergleichbarer Java-Code, sie sind aber auch schwierigerzu verstehen als beschreibende Methodennamen oder explizite Typangaben.Die vermeintliche Komplexität resultiert wohl vor allem aus der konsequenten Verknüpfung ob-jektorientierter und funktionaler Programmierung, die gleichzeitig auch Scalas größte Stärke ist.Denn sie bietet die Möglichkeit, die schon bei Java geschätzten Vorteile objektorientierter Pro-grammierung (wie die Strukturierung von Code oder die Wiederverwendbarkeit von Programm-komponenten durch Vererbung) mit dem hohen Abstraktionsniveau funktionaler Programmie-rung zu verbinden. Dies führt dazu, dass das Programmieren in Scala wiederum wesentlichintuitiver ist als in Java. Obwohl die funktionalen Konzepte ungewohnt scheinen, ermöglichensie sehr ausdrucksstarke Programmierung und eröffnen einem völlig neue Möglichkeiten zu Pro-blemlösungen. Zusätzlich machen sie den Code sehr robust und sicher. Eine weitere Stärke derProgrammiersprache Scala ist die Skalierbarkeit, die sich schon in der Unterstützung durch dieumfangreichen Standardbibliotheken zeigt. Scala lässt sich relativ einfach um gewünschte Funk-tionalitäten erweitern, ohne dass sich dabei das Gefühl nativer Sprachunterstützung verliert.Deshalb ist Scala auch sehr gut in Bereichen einsetzbar, in denen nicht der gesamte Umfang derSprache gefragt ist, sondern nur bestimmte Funktionalitäten und Schnittstellen zur Verfügunggestellt werden müssen.

Zusammenfassend eignet sich Scala sowohl für kleine Skripte, als auch für Projekte beliebigerGröße. Zwar erscheint Scala vorerst sehr komplex, profitiert aber enorm aus dem multiparadig-matischen Ansatz: Scala-Programme weisen oft einen hohen Abstraktionsgrad auf, sind kompaktund prägnant, strukturiert, kompatibel und portabel. Trotz der eher langsam zunehmenden Po-pularität der Sprache und in Hinblick auf die Tatsache, dass Scala ursprünglich auch als einbesseres Java konzipiert war, stellt sich allerdings die Frage, welche Bedeutung Scala in Zukunfthaben wird. Da sich Scala noch immer in aktiver Entwicklung befindet und noch längst kei-nen finalen Status erreicht hat, erscheint folgende Formulierung von Neal Gafter (ehemaligerEntwickler der Programmiersprache Java) jedoch treffend:

„Will Scala be the next great language? Only time will tell. Martin Odersky’s team certainly hasthe taste and skill for the job. One thing is sure: Scala sets a new standard against which futurelanguages will be measured.“ [OSV10]

Lasse Kristopher Meyer

Page 24: Die multiparadigmatische Programmiersprache Scalamh/lehre/bachelor_seminar...1In Scala können Methoden in Operatorenschreibweise definiert werden. Eine äquivalente Schreibweise

Literaturverzeichnis 21

Literaturverzeichnis

[Cam11] CAMELCASECON: Auswertung der Scala-Umfrage 2011. Deutschland : http://www.camelcasecon.de/pdf/scala/Auswertung_der_Scala_Umfrage.pdf, 2011. –Letzter Abruf: 11.01.2013

[Eck11] ECKEL, Bruce: Scala: The Static Language that Feels Dynamic. USA : http://www.artima.com/weblogs/viewpost.jsp?thread=328540, 2011. – Letzter Abruf:10.01.2013

[JPPV09] JENSON, Steve ; PAYNE, Alex ; POINTER, Robey ; VENNERS, Bill: Twitter on Scala. USA: http://www.artima.com/scalazine/articles/twitter_on_scala.html, 2009.– Letzter Abruf: 10.01.2013

[Ode06] ODERSKY, Martin: A Brief History of Scala. USA : http://www.artima.com/weblogs/viewpost.jsp?thread=163733, 2006. – Letzter Abruf: 08. Januar 2013

[OSV10] ODERSKY, Martin ; SPOON, Lex ; VENNERS, Bill: Programming in Scala: A comprehensivestep-by-step guide. Second Edition. USA : Artima, Inc., 2010. – ISBN 0–9815316–4–4,978–0–9815316–4–9

[OV09] ODERSKY, Martin ; VENNERS, Bill: The Origins of Scala: A Conversation with MartinOdersky, Part I. USA : http://www.artima.com/scalazine/articles/origins_of_scala.html, 2009. – Letzter Abruf: 08. Januar 2013

[Pla11] PLAY FRAMEWORK: Introducing Play 2.0. http://www.playframework.org/documentation/2.0.4/Philosophy, 2011. – Letzter Abruf: 10.01.2013

[Red12] REDMONK: The RedMonk Programming Language Rankings: September 2012. USA: http://redmonk.com/sogrady/2012/09/12/language-rankings-9-12/, 2012. –Letzter Abruf: 16.01.2013

[The10] THE FLYING FROG BLOG: ”Scala is foremost an industri-al language”. http://flyingfrogblog.blogspot.de/2010/08/scala-is-foremost-industrial-language.html, 2010. – Letzter Abruf:23.01.2013

[TIO13] TIOBE SOFTWARE: TIOBE Programming Community Index for January 2013. Nie-derlande : http://www.tiobe.com/index.php/content/paperinfo/tpci/, 2013.– Letzter Abruf: 09.01.2013

[Typ] TYPESAFE, INC.: Scala: The Language of Pragmatism. USA, Schweiz, Schweden : http://typesafe.com/technology/scala, . – Letzter Abruf: 12.01.2013

[Typ12] TYPESAFE, INC.: Scala in the Enterprise. USA, Schweiz, Schweden : http://www.scala-lang.org/node/1658#Sony, 2012. – Letzter Abruf: 16.01.2013

Lasse Kristopher Meyer