Teil 1: Einführung in Scala

Bevor wir uns tiefer in die Sprache rein tasten möchte ich euch ein wenig über die Sprache selbst und deren wichtigsten Tools erzählen:

Der Vater von Scala ist Martin Odersky, der die Arbeit an der Sprache zusammen mit seinem Team im Jahr 2001 aufnahm. Die Sprache wurde 2003 zum ersten Mal der Öffentlichkeit präsentiert, war zu dem Zeitpunkt aber noch weit entfernt von der Sprache, die wir heute kennen. Erst die Version 2.0, die 2006 fertig gestellt werden konnte, setzte den Startstein und machte Scala zu dem was wir heute kennen, mehr dazu in der Scala Version History.

Scala ist eine objektorientierte Programmiersprache, die sowohl imperatives als auch funktionales programmieren erlaubt. Zu bevorzugen ist allerdings das funktionale Paradigma, was uns in diesem Tutorial aber nicht groß stören soll – wir fangen wie versprochen imperativ an.

Die Tools von Scala ähneln von der Namensgebung her den Tools von Java. Da wären

  1. scalac, der Scala-Compiler
  2. scalap, ein Disassembler
  3. scaladoc, das Dokumentationswerkzeug
  4. sbaz, ein Paketverteilungssystem
  5. fsc, der Fast Scala Compiler. Dieses Tool beendet sich nicht, nachdem der Compiler seine Arbeit verrichtet hat, sondern besteht weiter in einem Hintergrundprozess. Dies ermöglicht ein schnelleres Starten des Compilers.
  6. REPL, die Read-Evaluate-Print Loop, einer interaktiven Konsole, in die Scala-Kommandos eingegeben werden können, die daraufhin sofort interpretiert und ausgeführt werden.

Es gibt drei Möglichkeiten Scala-Code auszuführen: über die REPL, als Skript und als vollwertige App. Die ersten beiden erfordern keine vorherige Kompilierung des Quelltextes – der Scala-Code wird einfach interpretiert und ausgeführt. Erst wenn wir Applikationen schreiben möchten müssen wir unseren Quelltext mit `scalac` zu JVM-Bytecode kompilieren und diesen dann von der JVM ausführen lassen. Falls wir den Code nur interpretieren lassen unterliegt er gewissen Einschränkungen, welche das genau sind soll uns für den Anfang aber nicht interessieren.

Für den Anfang wollen wir uns vor allem der REPL zuwenden, da sie einen schnellen Einstieg in die Sprache ermöglicht. Sie interpretiert alle Statements sofort nach der Eingabe und führt sie auch sofort aus. Wir haben also die Möglichkeit etwaige Fehler sofort zu erkennen und zu beseitigen. Die REPL kann mit dem Kommando `scala` gestartet werden. Der Aufruf wird einige Sekunden dauern, da zuerst noch die JVM gestartet werden muss. Nach dem Start sollte ein Willkommensdialog angezeigt werden, ähnlich dem folgendem:

Welcome to Scala version 2.9.1.RC1 (Java HotSpot(TM) 64-Bit Server VM, Java 1.7.0).
Type in expressions to have them evaluated.
Type :help for more information.

scala>

Wir können die REPL jederzeit mit dem Kommando `:q` verlassen, mehr dazu auch unter dem Hilfedialog, der mit `:help` aufgerufen werden kann.

Zuerst ein paar Testanweisungen:

scala> 3+5
res0: Int = 8

scala> (3).+(5)
res1: Int = 8

scala> 7.5-1.5
res2: Double = 6.0

scala> (7.5)-(1.5)
res3: Double = 6.0

scala> "hello"+"world"
res4: java.lang.String = helloworld

scala> ("hello").+("world")
res5: java.lang.String = helloworld

Wie zu sehen lassen sich alle Anweisungen auch in Objektsyntax aufrufen. Das hat einen ganz einfachen Grund: In Scala ist alles ein Objekt, also auch die primitiven Datentypen wie man sie von Java her kennt. Dieses Feature erlaubt uns z.B. innerhalb von Collections mit den gleichen Datentypen zu arbeiten und nicht wie bei Java auf Wrapperklassen umzusteigen. Es ist der Scala-Compiler, der uns dies abnimmt und selbst entscheidet wann ein Wrapper genutzt werden muss oder wann mit den primitiven Datentypen von Java gearbeitet werden kann. Da nur Objekte existieren werden alle Operationen – auch auf die primitive Datentypen der JVM – als Methoden eines Objektes angesehen. Es gibt also keine Operatoren im klassischen Sinne. Bei einer einfachen Addition wie `3+5` wird auf das Integerobjekt `3` die Methode `+` aufgerufen an die das Integerobjekt `5` als Parameter übergeben wird. Wie in Java kennzeichnet der Punkt einen Zugriff auf ein Objekt. Mit runden oder geschweiften Klammern können dann etwaige Parameter an eine Methode übergeben werden. Wann genau welches Klammerpaar genommen werden darf werde ich in einem anderen Kapitel noch erläutern. Es folgt also folgende Notation, die identisch ist mit der von Java (die geschweiften Klammern nicht berücksichtigt):

<object>.<method>(<param1>, <param2>, <paramN>)

In Scala gibt es bei der Syntax immer die Möglichkeit zwischen `operator notation` und `object notation` zu wählen. Ersteres wird auch `infix notation` genannt und wäre dabei ein Statement wie `3+5`, bei dem kein Punkt und keine Klammern notiert werden. Immer dann wenn von einem Punkt und den Klammern beim Zugriff auf ein Objekt Gebrauch gemacht wird, wird von `object notation` gesprochen. Ob und wann man die `access modifier` weglassen kann hängt von einer ganz bestimmten Regel ab, der `operator position`. Diese Regel tritt immer dann in Kraft wenn eine Methode nur einen oder gar keinen Übergabeparameter hat, was z.B. bei der schon genannten Addition der Fall ist. Sobald eine Methode mehr als einen Parameter erwartet müssen die Klammern explizit notiert werden, ansonsten beschwert sich der Compiler. Der Punkt hingegen kann nach wie vor weggelassen werden, es muss nur ein Leerzeichen gesetzt werden um dem Compiler die Möglichkeit zu geben Methoden- und Objektnamen zu unterscheiden. Die beiden folgenden Zeilen wären also identischer Code:

obj meth (param1, param2)
obj.meth(param1, param2)

wohingegen das Folgende ungültig wäre:

obj meth param1, param2

Wie vielleicht schon anhand des REPL-Outputs festgestellt gilt in Scala die Namenskonvention von `UpperCamelCase` für Klassen. So heißen de primitiven Datentypen `Int`, `Long`, `Boolean` etc. und werden nicht wie bei Java klein geschrieben. Diese Schreibweise beherbergt lediglich drei Ausnahmen: Die Schlüsselworte `true`, `false` und `null`, was aufgrund der Java-Interoperabilität so festgelegt wurde. Dennoch werden sie in Scala als Objekte angesehen, weshalb man bspw. folgendes schreiben kann:

scala> true.&&(false)
res6: Boolean = false

scala> false.||(true)
res7: Boolean = true

scala> null.==(5)
<console>:8: warning: comparing values of types Null and Int using `==' will always yield false
              null.==(5)
                     ^
res8: Boolean = false

Es ist in meinen Augen zwar ein störender aber zum Glück verschmerzbarer Bruch in Scalas Objektnotation, vor allem da Letzteres vorbildlicherweise sogar eine Warnung produziert.

Aber genug Theorie, wenden wir uns lieber wieder der REPL zu. Tippen wir mal das Folgende ein:

scala> "x"*5
res11: String = xxxxx

Huch, was ist das? Die Methode `*` gibt es in der Klasse String der JVM doch überhaupt nicht. Das ist richtig – hier kommt eine sogenannte implizite Konvertierung zum Einsatz, die unseren String in eine Wrapperklasse wandelt, die diese Methode besitzt. Die Erklärungen wie genau diese Umwandlung abläuft und welchen Regeln sie unterliegt möchte ich auf später verschieben und euch stattdessen mit der Beantwortung der Frage trösten wie ihr herausfindet welche Methoden ein Objekt besitzt. Zum einen kann euch dies die REPL beantworten. Sobald ihr ein Objekt mit einem dahinterfolgenden Punkt eintippt erscheinen alle expliziten Methoden des Objekts sobald ihr die Tabulatortaste betätigt:

scala> "hello".<tab>
+                     asInstanceOf          charAt                codePointAt           codePointBefore       codePointCount        compareTo
compareToIgnoreCase   concat                contains              contentEquals         endsWith              equalsIgnoreCase      getBytes
getChars              indexOf               intern                isEmpty               isInstanceOf          lastIndexOf           length
matches               offsetByCodePoints    regionMatches         replace               replaceAll            replaceFirst          split
startsWith            subSequence           substring             toCharArray           toLowerCase           toString              toUpperCase
trim

scala> "hello".

Wie unschwer zu erkennen steht aber nirgendwo etwas von der `*`-Methode. Dies kommt daher, weil die REPL die impliziten Konvertierungen innerhalb der Autovervollständigung nicht erkennt. Zum Glück bleibt uns aber auch noch der Blick in die API, die ihr unter scala-lang.org/api finden könnt. Die Warpperklasse, in die der String gewandelt wird, heißt `scala.collection.immutable.WrappedString`, die ihr komfotabel über die Suchfunktion in der oberen linke Ecke finden könnt. Innerhalb der Klasse findet ihr neben `*` noch viele weitere Methoden, die ihr auf ein String-Objekt anwenden könnt, z.B. `reverse`:

scala> "hello".reverse
res13: String = olleh

Es sei hier noch erwähnt, dass die impliziten Sprachfeatures das Herz von Scala ausmachen – sie stechen unter den anderen Features der Sprache besonders hervor, da sie enorme Möglichkeiten für einen eleganten Codeaufbau bieten und die Scala-Collections zu der wohl mächtigsten Collections-Bibliothek machen unter allen heutigen verfügbaren Programmiersprachen.
An dieser Stelle bleibt mir wieder nichts anderes übrig darauf zu verweisen, dass es noch nicht an der Zeit ist mehr über `implicits` zu beschreiben, da sonst nur wieder der Eindruck von einer zu komplexen Sprache entstehen wird. Sie sind ein Feature, das man aus Sprachen wie C und Java nicht kennt und deshalb am Anfang nur für unnötige Verwirrung sorgen würde.

Advertisements

2 comments so far

  1. Sebi on

    Hallo Antoras,

    vielen Dank für deinen Blog. Wenn ich deinen Code richtig verstehe, hast du in deinem zweiten Code-Beispiel beim vierten Befehl

    scala> (7.5)-(1.5)
    res3: Double = 6.0
    

    einen Punkt vergessen. Um die Methode ‚-‚ über die Objektnotation aufzurufen müsste (7.5).-(1.5) dastehen. Liege ich mit meiner Vermutung richtig?

    Viele Grüße
    Sebi

    • Simon Schäfer on

      Spielt in dem Beispiel keine Rolle ob der Punkt vorhanden ist oder nicht. Es funktioniert beides.


Kommentar verfassen

Trage deine Daten unten ein oder klicke ein Icon um dich einzuloggen:

WordPress.com-Logo

Du kommentierst mit Deinem WordPress.com-Konto. Abmelden / Ändern )

Twitter-Bild

Du kommentierst mit Deinem Twitter-Konto. Abmelden / Ändern )

Facebook-Foto

Du kommentierst mit Deinem Facebook-Konto. Abmelden / Ändern )

Google+ Foto

Du kommentierst mit Deinem Google+-Konto. Abmelden / Ändern )

Verbinde mit %s

%d Bloggern gefällt das: