Fallklassen-Copy


Eine der nettesten Erweiterungen in Scala 2.8 sind benannte und Default-Argumente. Bei einem Default-Argument wird einfach bei den Methoden-Parametern ein Default-Wert angegeben – eine schöne Sache, insbesondere wenn man in Java durch das Schreiben von hunderttausend überladenen Konstruktoren ausreichend traumatisiert ist. Benannte Argumente erlauben es, Methoden-Argumente nicht wie üblich der Reihenfolge nach, sondern über ihren Namen aufzurufen – sehr praktisch, wenn man sich (wie ich) selbst nach Jahren nicht die Reihenfolge der JOptionPane-Dialog-Parameter merken kann.

Beides zusammen erlaubt einen neues Feature, nämlich eine copy-Methode für Fallklassen. Man kann sich sicher schon denken, wie es funktioniert: Der Compiler generiert eine Methode copy, die eine neues Objekt der Fallklasse erstellt. Dabei sind die Werte der aktuellen Instanz die Default-Werte von copy. Dank benannter Argumente kann man sich dann die Elemente herauspicken, die man wirklich verändern will. Wenn die Argumente schon in der passenden Reihenfolge sind (man also die letzen Argumente unverändert übernehmen will), kann man copy natürlich auch ganz normal ohne benannte Argumente aufrufen und einfach die letzten Argumente weglassen (ein bisschen so wie bei Varargs).

Bei der Suche nach einem Anwendungbeispiel bin ich über die Tape-Klasse aus meinem Brainfuck-Artikel gestolpert. Ich hatte damals schon überlegt, ob ich sie zu einer Fallklasse machen sollte, aber das hätte nicht viel gebracht. Hier noch einmal das Original:

class Tape[T](val cell:T, private val left:List[T], private val right:List[T], val func:Func[T]) {
    
    def inc = new Tape(func.inc(cell),left, right, func )
    def dec = new Tape(func.dec(cell),left, right, func)
    def goLeft = left match {
        case Nil => new Tape(func.zero,Nil, cell :: right, func)
        case head :: tail => new Tape(head, tail, cell :: right, func )
    }
    def goRight = right match {
        case Nil => new Tape(func.zero, cell:: left, Nil, func)
        case head :: tail => new Tape(head, cell :: left, tail, func)
    }
    def output = {
         func.out(cell)
         this
    }
    def input() = new Tape(func.in, left, right, func)
    def isZero = cell == func.zero
}

Und hier die Fallklassen-Version, die die neue copy-Methode benutzt:

case class Tape[T](cell:T, left:List[T], right:List[T], func:Func[T]) {
    
    def inc = copy(cell = func.inc(cell))
    def dec = copy(cell = func.dec(cell))
    def goLeft = left match {
        case Nil => copy(func.zero, Nil, cell :: right)
        case head :: tail => copy(head, tail, cell :: right)
    }
    def goRight = right match {
        case Nil => copy(func.zero, cell :: left, Nil)
        case head :: tail => copy(head, cell :: left, tail)
    }
    def output = {
         func.out(cell)
         this
    }
    def input() = copy(cell = func.in)
    def isZero = cell == func.zero
}

Auch wenn sich die Zeilenzahl nicht geändert hat, ist die Klasse übersichtlicher geworden. Insbesondere der eigentlich nur „durchgeschleifte“ Func-Parameter taucht beim Kopieren gar nicht mehr auf.

Ich finde, dass diese neue Funktionalität die Arbeit mit Fallklassen sehr erleichtert. Eine copy-Methode lässt sich natürlich leicht selbst schreiben, wenn eine Fallklasse aus irgendwelchen Gründen nicht passen sollte. Davon unabhängig können Default-Argumente helfen, die Anzahl überladener Methoden zu reduzieren und zur besseren Lesbarkeit beizutragen. Benannte Argumente helfen nicht nur den Vergesslichen, sondern können gerade bei längeren Argumentlisten mit wenig aussagekräftigen Typen deutlich machen, was da eigentlich vorgeht.

Meiner Meinung nach sind dies die Features, die den höchsten Spaßfaktor-Zuwachs in Scala 2.8 bringen werden (dicht gefolgt von Manifests). Viel Spaß beim Ausprobieren!

Advertisements

Ein Gedanke zu “Fallklassen-Copy

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