By-Name-Parameter zur Auswertung von Parametern je nach Bedarf

Sugartime

Arno Haase


Sehen wir uns eine extrem vereinfachte Logger-Implementierung an. Sie hat ein Flag, mit dem man das Debug-Logging ein- und ausschalten kann:

object Logger { var isDebugEnabled = true def debug(msg: AnyRef) { if (isDebugEnabled) { println(msg) } }}

Damit kann man beliebige Nachrichten loggen. Wenn das Erzeugen der Nachricht aber teuer ist – z. B. die Anzahl der Einträge in einem Dateiverzeichnis beinhaltet – kann es sinnvoll sein, diese Kosten zu vermeiden, wenn das Logging gerade abgeschaltet ist. Man kann dazu den Aufruf in ein if verpacken:

if (Logger.isDebugEnabled) { Logger.debug("Anzahl Dateien: " + new File(".").list().size)}

Das funktioniert und ist in Java gängige Praxis, macht das Logging aber komplizierter. Hier wäre es nützlich, wenn der Logger selbst entscheiden könnte, ob der Parameter überhaupt evaluiert wird. Genau für solche Situationen, in denen man einen Wert übergeben will, der Aufrufer aber die Kontrolle über die Auswertung der übergebenen Expression haben soll, gibt es in Scala By-Name-Parameter. Dazu stellt man einem Parametertyp => voran:

def debug(msg: => AnyRef) { if (isDebugEnabled) { println(msg) }

Eine While-Schleife als Beispiel

Ein By-Name-Parameter ist intern eine vollwertige Funktion. Sie wird jedes Mal ausgewertet, wenn der Parameter verwendet wird. Das eröffnet vielfältige Möglichkeiten zur Gestaltung von Syntax. Die folgende Implementierung einer While-Schleife (in Anlehnung an [1]) soll das illustrieren. Sie tut in dieser Form nichts anders als das eingebaute while. Aber wir können uns ja vorstellen, dass sie zusätzlich Dinge wie Protokollierung, Transaktionsbehandlung o. Ä. erledigt.

@tailrecdef myWhile(cond: => Boolean)(body: => Unit): Unit = { if(cond) { body myWhile(cond)(body) }}

Diese Methode hat zwei Parameter, die beide by-name sind, nämlich die Bedingung und den Body der Schleife. Die Implementierung ist rekursiv, um nicht platt das eingebaute while-Konstrukt von Scala zu verwenden. Sie ist sogar tail-rekursiv [2], sodass diese Implementierung keinen Performancenachteil gegenüber iterativem Code hat.

Die Methode wertet die Bedingung aus, und im Erfolgsfall führt sie den Body aus und ruft sich selbst rekursiv mit denselben Parametern auf. Auf beide Parameter wird also potenziell mehrmals zugegriffen, und Scala wertet sie jedes Mal neu aus. Wir können unsere neue Schleife jetzt genau so verwenden wie das eingebaute while-Konstrukt:

var i=0myWhile (i

Behandlung als Funk...

By-Name-Parameter zur Auswertung von Parametern je nach Bedarf

Sugartime

Arno Haase


Sehen wir uns eine extrem vereinfachte Logger-Implementierung an. Sie hat ein Flag, mit dem man das Debug-Logging ein- und ausschalten kann:

object Logger { var isDebugEnabled = true def debug(msg: AnyRef) { if (isDebugEnabled) { println(msg) } }}

Damit kann man beliebige Nachrichten loggen. Wenn das Erzeugen der Nachricht aber teuer ist – z. B. die Anzahl der Einträge in einem Dateiverzeichnis beinhaltet – kann es sinnvoll sein, diese Kosten zu vermeiden, wenn das Logging gerade abgeschaltet ist. Man kann dazu den Aufruf in ein if verpacken:

if (Logger.isDebugEnabled) { Logger.debug("Anzahl Dateien: " + new File(".").list().size)}

Das funktioniert und ist in Java gängige Praxis, macht das Logging aber komplizierter. Hier wäre es nützlich, wenn der Logger selbst entscheiden könnte, ob der Parameter überhaupt evaluiert wird. Genau für solche Situationen, in denen man einen Wert übergeben will, der Aufrufer aber die Kontrolle über die Auswertung der übergebenen Expression haben soll, gibt es in Scala By-Name-Parameter. Dazu stellt man einem Parametertyp => voran:

def debug(msg: => AnyRef) { if (isDebugEnabled) { println(msg) }

Eine While-Schleife als Beispiel

Ein By-Name-Parameter ist intern eine vollwertige Funktion. Sie wird jedes Mal ausgewertet, wenn der Parameter verwendet wird. Das eröffnet vielfältige Möglichkeiten zur Gestaltung von Syntax. Die folgende Implementierung einer While-Schleife (in Anlehnung an [1]) soll das illustrieren. Sie tut in dieser Form nichts anders als das eingebaute while. Aber wir können uns ja vorstellen, dass sie zusätzlich Dinge wie Protokollierung, Transaktionsbehandlung o. Ä. erledigt.

@tailrecdef myWhile(cond: => Boolean)(body: => Unit): Unit = { if(cond) { body myWhile(cond)(body) }}

Diese Methode hat zwei Parameter, die beide by-name sind, nämlich die Bedingung und den Body der Schleife. Die Implementierung ist rekursiv, um nicht platt das eingebaute while-Konstrukt von Scala zu verwenden. Sie ist sogar tail-rekursiv [2], sodass diese Implementierung keinen Performancenachteil gegenüber iterativem Code hat.

Die Methode wertet die Bedingung aus, und im Erfolgsfall führt sie den Body aus und ruft sich selbst rekursiv mit denselben Parametern auf. Auf beide Parameter wird also potenziell mehrmals zugegriffen, und Scala wertet sie jedes Mal neu aus. Wir können unsere neue Schleife jetzt genau so verwenden wie das eingebaute while-Konstrukt:

var i=0myWhile (i

Behandlung als Funk...

Neugierig geworden?


    
Loading...

Angebote für Teams

Für Firmen haben wir individuelle Teamlizenzen. Wir erstellen Ihnen gerne ein passendes Angebot.

Das Library-Modell:
IP-Zugang

Das Company-Modell:
Domain-Zugang