Game of Life mit Scala – das Tutorial

Bevor wir mit unserem Game of Life beginnen, wollen wir noch rasch den Klassiker schlechthin programmieren und ausführen. Dazu erstellen wir die Datei Hello.scala im Verzeichnis src/main/scala und schreiben darin den folgenden Code:

object Hello {
  def main(args: Array[String]) {
    println("Hello World!")
  }
}

Anschließend rufen wir in SBT run auf und nehmen den altbekannten Gruß entgegen.

Klassen und Packages

Als Java-Entwickler sind wir mit der Objektorientierung bestens vertraut. Daher nähern wir uns Scala auch zunächst aus dieser Richtung. Das ist möglich, da Scala als hybride Sprache die Objektorientierung mit der funktionalen Programmierung [8] vereint. Wir klammern in diesem Tutorial theoretische Details aus, aber ein paar wichtige Grundlagen zur Objektorientierung in Scala befinden sich im Kasten „Objektorientierung pur“.

Objektorientierung pur

Scala setzt das OO-Prinzip wesentlich strenger um als Java. Das bedeutet, dass alles, aber wirklich alles, ein Objekt ist. Daher gibt es auch keine primitiven Datentypen und kein static. Und es gibt auch keine Operatoren (die Zuweisung ist eine Ausnahme), sondern nur Methoden.

So besteht zum Beispiel die Addition zweier ganzer Zahlen nicht aus zwei Primitiven und einem Plus-Operator, sondern aus zwei Int-Objekten und dem Aufruf der Methode +, die für Int definiert ist.

Dank der flexiblen Syntax von Scala müssen wir nicht 1.+(2) schreiben, was aber durchaus möglich wäre, sondern können wie gewohnt 1 + 2 notieren, indem wir einfach den Punkt und die Klammern weglassen. Das nennt sich Infix-Operator-Notation und ist immer dann möglich, wenn eine Methode genau einen Parameter hat.

Anstelle von static kennt Scala Singleton Objects, die quasi gleichzeitig eine Klasse und eine Instanz davon definieren. Diese sind vollwertige Objekte und können u. a. auch als Argument für Methodenaufrufe verwendet werden.

Das Game of Life dreht sich um tote oder lebendige Zellen. Daher bietet sich als erste einfache Abstraktion die Klasse Cell an, die wir in der Datei Cell.scala im Source-Verzeichnis src/main/scala anlegen wollen. In Scala definieren wir Klassen wie in Java mit dem Schlüsselwort class: class Cell.

Wir beginnen mit einer leeren Klasse, ganz ohne Felder und Methoden. Deshalb können wir die geschweiften Klammern für den „Rumpf“ der Klasse erst einmal weglassen und ebenso den Strichpunkt, der in Scala dank der so genannten Semicolon Inference fast niemals benötigt wird. Anders als in Java brauchen wir auch kein public, denn das gibt es in Scala nicht; öffentlicher Zugriff ist der Standard.

Auch in Scala werden Klassen in Packages organisiert. Diese sind zwar mächtiger, in ihrer einfachsten Form aber ganz ähnlich wie in Java. Ein wichtiger Unterschied: Es besteht nicht die Notwendigkeit, dass Package- und Verzeichnisstruktur identisch sind. Es bietet sich an, das typischerweise pro Projekt immer gleiche Wurzel-Packages bei der Verzeichnisstruktur wegzulassen, aber darunterliegende Packages sehr wohl im Dateisystem widerzuspiegeln. Hier wollen wir com.weiglewilczek.gameoflife als einziges Package verwenden, sodass wir überhaupt keine Unterverzeichnisse benötigen und unsere Datei Cell.scala bereits am richtigen Ort liegt. Damit sieht unsere Zelle bisher folgendermaßen aus:

package com.weiglewilczek.gameoflife
class Cell
Die REPL

Natürlich können wir damit noch nicht allzu viel anfangen, aber wir wollen dennoch auf Basis dieses Standes ein wichtiges Scala-Werkzeug kennenlernen: Die interaktive Konsole, auch Read Evaluate Print Loop (REPL) genannt. Mit SBT können wir diese besonders bequem so starten, dass unser Projekt gleich Bestandteil des Klassenpfads ist, indem wir einfach console in die SBT-Konsole eingeben. Nun können wir zeilenweise Scala-Code eingeben, der von der REPL interpretiert und ausgeführt wird. Daneben gibt es einige REPL-Kommandos, die wir mit :help auflisten können. Als Erstes wollen wir unser Package importieren. Dazu nutzen wir das Schlüsselwort import und geben, durch ein Leerzeichen getrennt und am besten unter Verwendung der automatischen Vervollständigung mithilfe der Tab-Taste, unser Package ein:

scala> import com.weiglewilczek.gameoflife._
import com.weiglewilczek.gameoflife._

Hier bedeutet der Unterstrich, dass der komplette Inhalt des Package importiert wird, analog zum „*“ in Java. Nun wollen wir eine neue Instanz einer Zelle anlegen. Das geht wie in Java mit dem Schlüsselwort new, allerdings können wir uns die Klammern nach dem Klassennamen sparen, weil wir noch keine Klassenparameter (siehe weiter unten) bzw. Konstruktorargumente haben:

scala> new Cell
res0: com.weiglewilczek.gameoflife.Cell = com...

Wie wir sehen, erzeugt die REPL automatisch eine neue Variable namens res0 (wie wir explizit einen anderen Namen verwenden können, sehen wir später) und gibt deren Typ sowie das Ergebnis der toString-Methode aus. Da wir mit unserer Zelle an dieser Stelle nicht viel anfangen können, verlassen wir die REPL jetzt mittels :quit (oder einfach nur :q) und wenden uns wieder dem Code zu, um unseren Zellen Leben bzw. Bedeutung einzuhauchen.

Kommentare

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.