Higher Order Messaging: Arrays auf Steroiden

Seit ein paar Monaten gibt es in Laravel ein neues Feature: Higher Order Messaging in Collections. Um zu verstehen, was das ist und worum es sich hierbei handelt, muss erst einmal eine Collection erklärt werden.

Wer noch nicht mit Laravel gearbeitet hat, kennt sie vermutlich nicht. Aber im Endeffekt sind Collections einfach getunte PHP-Arrays. Also eine Wrapper-Klasse um ein normales Array mit einigen Zusatzfunktionen. Statt array_map($function, $array) kann man hier beispielsweise einfach $collection->map($function) ausführen. Und so gibt es einige zusätzliche Methoden: filter, sum, where, first, pull, etc. Die ganze Liste gibt es hier. Zusammenfassend lässt sich sagen: Collections vereinen alle Array-Funktionen von PHP in einem Objekt, und vereinfachen so die Arbeit mit Arrays enorm.

Das neuste Feature von Collections ist - wie eingangs erwähnt - das Higher Order Messaging. Das klingt erstmal kompliziert, und ist in der Tat auch relativ seltsam. Ein Beispiel erklärt das Ganze etwas besser. Angenommen wir haben eine Collection $employees, welche ein Array von Mitarbeiter-Objekten enthält. Ein Mitarbeiter hat die Methode sendPayment(), um eine Bezahlung zu senden.

Möchten wir nun jedem Mitarbeiter eine Bezahlung senden, haben wir mehrere Möglichkeiten dies zu tun:

Mit einer For-Each-Schleife

foreach ($employees as $employee) {
    $employee->sendPayment();
}

Mit einer Map-Funktion:

$employees->map(function ($employee) {
    $employee->sendPayment();
});

Oder mit Higher Order Messaging:

$employees->each->sendPayment();

Hoppla. Das sieht komisch aus. Was passiert hier? Warum hat die $employees-Collection ein each-Attribut? Und warum sollte dieses Attribut eine sendPayment()-Methode haben?

Gute Frage! In Wahrheit hat die Collection weder ein each-Attribut, noch eine sendPayment()-Methode. Was passiert ist, dass intern eine Schleife gestartet wird, welche über jedes Mitarbeiterobjekt geht und die gewünschte Methode dort ausführt. Technisch wird dies realisiert, indem ein HigherOrderProxy-Objekt zurückgegeben wird, sobald das each-Attribut aufgerufen wird. Jeder Methodenaufruf auf dieses Objekt wird dann an die each-Methode der Collection weitergeleitet, welche diese anschließend auf dem Mitarbeiterobjekt ausführt.

Wer jetzt denkt “das ist aber sehr von hinten durch die Brust ins Auge”, der darf sich freuen: It gets worse:

$employees->reject->retired->each->sendPayment();

Erst wird mittels reject jeder Mitarbeiter, welcher das retired-Attribut auf true gesetzt hat, aus der Collection entfernt. Anschließend werden alle Übrigen bezahlt. Logisch, oder?

Mein Problem

Mein Problem mit Higher Order Messaging ist, dass es sehr unkonventionell ist. Wer nicht weiß, dass so etwas wie HOM existiert, wird bei dieser Zeile absolut auf die Nase fliegen. Das ist in meinen Augen nicht das Ziel von “Selsbterklärendem Code”, auch wenn es wirklich wesentlich einfacher ist, als die “klassische Alternative“.

Außerdem ist die technische Realisierung zwar wirklich sehr geschickt und schlau umgesetzt, lässt sich aber mit Magie vergleichen. Wer nicht weiß, wie dies im Hintergrund umgesetzt wird, kann bei einem eventuellen Fehlverhalten ebenfalls große Probleme bekommen.

Ich möchte dieses neue Feature aber auch nicht ablehnen. Ich finde gerade Aktionen wie each, sum oder contains sehr elegant und simpel. Außerdem ist es definitiv nicht falsch, dass Programmiersprachen und Techniken weiterentwickelt werden um uns Entwicklern das Leben zu vereinfachen, auch wenn wir dadurch neue Skills lernen müssen. Doch wo ist die Grenze?