Samstag, 19. März 2016

Closures in Clojure

Eine Sprache, die nach Closures benannt ist (und genauso ausgesprochen wird), sollte ziemlich gut mit Closures umgehen können :-). Das Zähler-Beispiel für Java, Groovy, Scala etc. sieht in Clojure wie folgt aus:

; closures.clj

(defn zaehlerMitStartwert [startwert]
  (let [zaehler (atom startwert)]
    (fn [] (swap! zaehler inc))))

(def next1 (zaehlerMitStartwert 10))
(def next2 (zaehlerMitStartwert 20))

(println (next1))  ;; 11
(println (next2))  ;; 21
(println (next2))  ;; 22
(println (next1))  ;; 12

In der Funktion zaehlerMitStartwert wird im let-Block ein lokales Binding für zaehler angelegt. Der Wert von zaehler ist ein Atom mit dem übergebenen startwert. Ein Atom speichert in Clojure einen Zustand, der threadsicher ausgetauscht werden kann. Rückgabe von zaehlerMitStartwert ist eine Funktion ohne Parameter: (fn [] ...) - dies ist die Closure, die die freie Variable zaehler außerhalb der Closure-Funktion bindet (umschließt). Die Closure-Funktion verändert (swap!t) den zaehler-Wert mithilfe der inc-Funktion und gibt den neuen (reingeswap!ten) Wert zurück.

Dann definieren wir zwei globale Variablen next1 und next2, die mit je einer Closure-Instanz initialisiert werden. Diese Funktionen werden mit der Clojure- bzw. Lisp-typischen Syntax (next1) aufgerufen (statt next1() o.ä.).

Mittwoch, 16. März 2016

Closures in Groovy

Beim JavaLand 2016 vor einer Woche hat Dierk König einen schönen Einstieg in die elegante und kompakte Ausdrucksweise von Groovy gegeben. Dabei hat er als Closure-Beispiel einen Zähler verwendet, den ich hier – weil er so gut zu meinen bisherigen Closure-Beispielen (u.a. für Java, JavaScript und Scala) passt – zeigen möchte (Fehler im Code gehen selbstverständlich auf mein Konto ;-):

// closures.groovy

Closure zaehlerMitStartwert(startwert) {
  return {
    startwert++
  }
}

def next1 = zaehlerMitStartwert(10)
def next2 = zaehlerMitStartwert(20)

println next1()  // 10
println next2()  // 20
println next2()  // 21
println next1()  // 11

Das return in der Funktion zaehlerMitStartwert gibt die Closure – den darauffolgenden Block mit geschweiften Klammern und der Zähl-Anweisung – zurück. Im Block selbst ist kein return notwendig, weil hier für den Compiler klar ist, dass die letzte (und hier einzige) Anweisung im Block das Ergebnis der Block-Funktion ist. Man sieht sehr schön, dass Groovy-Closures problemlos freie Variablen außerhalb des Closure-Blocks binden - so, wie das auch bei Scala der Fall ist (aber anders als bei Java, wo normalerweise nur final-Werte und nur mit Tricks Variablen gebunden werden können).

Wer das Groovy-Skript ausprobieren möchte, kann es einfach in die Groovy-Web-Console kopieren und dort ausführen.

Dienstag, 1. März 2016

Closures in Java

Java 8 ist nun bald zwei Jahre alt – eine gute Gelegenheit, meine kleine Closures-Reihe weiterzuführen. Nach Scala, JavaFX und JavaScript sind heute also Closures in Java an der Reihe. Das Zähler-Beispiel sieht mit den Lambda-Ausdrücken aus Java 8 so aus:
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.IntSupplier;

public class ClosuresJava8 {

  static IntSupplier zaehlerMitStartwert(int startwert) {

    AtomicInteger n = new AtomicInteger(startwert);

    IntSupplier s = () -> {
      return n.incrementAndGet();
    };

    return s;
  }

  public static void main(String[] args) {

    IntSupplier next1 = zaehlerMitStartwert(10);
    IntSupplier next2 = zaehlerMitStartwert(20);

    System.out.println( next1.getAsInt() ); // 11
    System.out.println( next2.getAsInt() ); // 21
    System.out.println( next2.getAsInt() ); // 22
    System.out.println( next1.getAsInt() ); // 12
  }
}
Die Methode zaehlerMitStartwert() gibt eine IntSupplier-Lambda-Funktion zurück. IntSupplier ist ein sogenanntes "funktionales Interface", d.h. ein Interface mit genau einer Methode (in diesem Fall getAsInt()). Ein "Supplier" bezeichnet dabei eine Methode ohne Parameter, die einen Rückgabewert liefert.

Der Lambda-Ausdruck ist dabei so etwas wie eine Kurzschreibweise für ein anonymes Objekt des IntSupplier-Interfaces, wobei das new, der Interface-Name und der Methodenname weggelassen werden. Die Anweisung im Lambda-Ausdruck ("return n.incrementAndGet();") wird dabei zum Rumpf der unsichtbaren getAsInt()-Methode.

Im Lambda-Ausdruck wird der übergebene Startwert erhöht und zurückgegeben. Allerdings müssen die gebundenen Variablen außerhalb des Lambda-Ausdrucks in Java final oder "effectively final" sein (d.h. sie dürfen nicht verändert werden, damit der Compiler final annehmen kann). Deshalb lassen wir den Lambda-Ausdruck ein AtomicInteger-Objekt binden – der Inhalt des AtomicInteger-Objektes ist veränderbar, die Referenz darauf ist aber wie gewünscht final.

Im obigen Beispiel wird die Lambda-Funktion nur zum einfacheren Verständnis in der lokalen Variablen s zwischengespeichert. Man kann den Lambda-Ausdruck auch direkt als Methodenergebnis zurückgeben:
  static IntSupplier zaehlerMitStartwert(int startwert) {

    AtomicInteger n = new AtomicInteger(startwert);

    return () -> {
      return n.incrementAndGet();
    };
  }
Und da unser Lambda-Ausdruck nur aus einer einzigen Anweisung besteht, deren Ergebnis als Funktionsergebnis zurückgegeben wird, kann man das noch weiter kürzen und die geschweiften Klammern und das return im Lambda-Rumpf weglassen:
  static IntSupplier zaehlerMitStartwert(int startwert) {

    AtomicInteger n = new AtomicInteger(startwert);

    return () -> n.incrementAndGet();
  }
Das sieht doch schon fast richtig funktional aus :-)