Meine liebste Programmiersyntax: „Pipelining“
(herecomesthemoon.net)- Pipelining ist in Programmiersprachen eine wichtige Funktion, die die Lesbarkeit und Wartbarkeit von Code erhöht
- Es ermöglicht, Datenflüsse natürlich von links nach rechts und von oben nach unten auszudrücken
- In Sprachen wie Rust macht Pipelining den Codefluss klarer und steigert durch die Autovervollständigung in der IDE die Produktivität der Entwicklung
- Es wird in vielen Sprachen wie Haskell, Elm und SQL eingesetzt; auch das Builder-Pattern oder Method Chaining gelten als eine Form von Pipelining
- Es wirkt sich positiv auf Lesbarkeit, Bearbeitbarkeit, IDE-Unterstützung und Versionsverwaltungswerkzeuge (diff, blame) aus
- Es erlaubt, im Vergleich zu verschachtelten Funktionsaufrufen knapperen und klareren Code zu schreiben, was auch für Zusammenarbeit und Wartung vorteilhaft ist
Meine liebste Programmiersyntax: Pipelining
Was ist Pipelining?
- Eine Funktion, bei der ein vorheriger Wert weitergereicht wird, sodass in der Parameterliste ein Argument ausgelassen werden kann
- Erhöht die Lesbarkeit des Codes und macht es einfacher, Kommentare hinzuzufügen
- Ein Syntaxstil, bei dem aufeinanderfolgende Verarbeitungsschritte sequenziell auf Daten angewendet werden
- In funktional geprägtem Code wird dies häufig in Form von Method Chaining wie
.map().filter().collect()verwendet - In Rust ist zum Beispiel folgender Code ein typisches Beispiel:
data.iter() .filter(|w| w.alive) .map(|w| w.id) .collect() - Werden dagegen alle Funktionen verschachtelt, entsteht eine Struktur, die von innen nach außen gelesen werden muss:
collect(map(filter(iter(data), |w| w.alive), |w| w.id))
Warum ist Pipelining gut?
-
1. Lesbarkeit und Wartbarkeit
- Leicht von oben nach unten zu lesen → Datenfluss in derselben Reihenfolge, in der Menschen lesen
- Zu jeder Zeile lassen sich leicht Kommentare hinzufügen
- Knapp und klar ohne Klammer-Verschachtelung in langen Zeilen
-
2. Einfaches Bearbeiten
- Neue Funktionen wie
.map()lassen sich zwischendrin einfach als eigene Zeile hinzufügen - Auch in
git diffodergit blamelassen sich Änderungen sauber nachvollziehen
- Neue Funktionen wie
-
3. IDE- / LSP-Unterstützung
- Passt gut zu einer Struktur, bei der beim Drücken der
.-Taste eine Autovervollständigungsliste erscheint - Vorteilhaft für statische Analyse, bei der die Typen klar bekannt sein müssen
- Damit diese Funktion richtig arbeitet, sollte die Sprache auf statischen Typen basieren (z. B. Rust, TypeScript)
- Passt gut zu einer Struktur, bei der beim Drücken der
Pipelining auch in SQL?
- Es gibt Vorschläge, verschachtelte SQL-
SELECT-Abfragen in einen Pipeline-Stil umzuwandeln - Beispiel:
FROM customer |> LEFT OUTER JOIN orders ON ... |> AGGREGATE COUNT(...) GROUP BY ... |> ORDER BY ... - Klarerer Ablauf und bessere Lesbarkeit als in herkömmlichem SQL
- Nachteil: Wenn die
SELECT-Anweisung nach oben verschoben wird, kann der Rückgabetyp schwerer zu erkennen sein → lässt sich lösen
Verbindung zum Builder-Pattern
- Formen wie
Builder::new().option().option().build()in Rust sind typische Pipeline-Strukturen - Wenn optionale Konfigurationen als Methoden aufgebaut werden, wird Codeverfolgung und Änderungsverwaltung einfacher
Verbesserungen beim Pipelining in Haskell
- Operatoren wie
$,&,|>in Haskell ermöglichen die Nutzung von Pipelines statt Funktionskomposition - Vergleich vorher/nachher:
-- 기존 checkPalindromes content = unlines $ map (show . isPalindrome) $ lines $ map toLower content -- 개선 checkPalindromes content = content & map toLower & lines & map (show . isPalindrome) & unlines
Vorteile von Pipelining in Rust
- Method Chaining, Typinferenz und strukturelle Erweiterbarkeit auf Trait-Basis harmonieren sehr gut mit Pipelining
- Rust wirkt wie eine Sprache, die die Stärken funktionaler und objektorientierter Syntax vereint, weshalb sich Pipelining dort besonders natürlich anfühlt
Fazit
- Pipelining ist nicht bloß einfache Syntax, sondern eine Kernfunktion, die Codefluss, Bearbeitbarkeit und Zusammenarbeit beeinflusst
- Statt Verschachtelung wie
f(g(h(x)))ist eine Struktur wiex |> h |> g |> fmenschenfreundlicher - Unter der einfachen Regel „eine Aufgabe pro Zeile“ ist Pipelining die beste Methode, natürliche Abläufe auszudrücken
„Jedes Stück der Pipeline nimmt die Hauptdaten entgegen und führt genau eine Aufgabe aus. Gibt man dem Ergebnis am Ende einen klaren Namen, ist das die ideale Codestruktur.“
11 Kommentare
https://github.com/tc39/proposal-pipeline-operator
Das dürfte ein ähnlicher Zusammenhang sein wie bei beliebigem Text,
bei dem Zeilenumbrüche und Einrückungen wichtig für die Lesbarkeit sind.
LINQ ist das Beste!
Gleam unterstützt das auch, sodass man ziemlich sauber Code schreiben kann.
Übrigens wird wegen des Codeblocks im Haupttext anscheinend auch auf dem Smartphone das Desktop-Layout angezeigt.
Wenn man darüber nachdenkt, geht das auch in elm.
Bei kleinen Datenmengen und einfachem Code wie im obigen Beispiel sieht es meiner Meinung nach gut aus und hat keine besonderen Nachteile.
Wenn jedoch nach und nach Code in
map()hineinkommt, besteht die Tendenz, dass der Code immer aufgeblähter wird, und je nach Sprache oder verwendeter Bibliothek kann es bei größeren Datenmengen leicht tausendfach langsamer werden als eine Verarbeitung, bei der man Daten einfach in einer Datenstruktur sammelt oder manipuliert.Außerdem gibt es noch einen neuen Grund, warum ich es nicht mehr bevorzuge: Als ich diesen Artikel auf dem Smartphone angesehen habe, blieb die Breite auf PC-Niveau, sodass die Schrift winzig klein wurde und der Artikel deshalb sehr schwer zu lesen war T.T
Grundsätzlich bevorzuge ich es nicht und bemühe mich auch nicht absichtlich, so etwas auf diese Weise zu schreiben.
Gebt uns auch etwas
js|> verbeug, verbeug|>ist einfach wunderschönIst die Syntax, die ich am meisten hasse
Schon wenn der Stacktrace nur ein bisschen verschachtelt ist, ist das Debugging die Hölle
Stimmt absolut.
Hacker-News-Kommentare
Der Autor nennt es zwar "pipelining", aber ich denke, der korrekte Begriff ist "method chaining"
Ich persönlich befürworte es, den Funktionsumfang einer Sprache klein zu halten und schnell einen abgeschlossenen Feature-Satz zu erreichen
|>-Syntax von Elixir übernehmenLisp-Makros bieten eine allgemeine Lösung, mit der sich nicht nur verkettete Kollektionsoperatoren, sondern auch die Reihenfolge von Aufrufketten festlegen lässt
Ich habe diesen Begriff als Fluent Interface gelernt. Pipelining ist etwas anderes
Der Pipeline-Operator ist eine Art partielle Anwendung: Man kann mehrere Argumente binden, um eine neue Funktion zu erzeugen, und deren Ausgabe an eine andere Funktion weitergeben
Nutzer des tidyverse in R verwenden das bereits
Pipelining ist schwer zu debuggen. Ausnahmebehandlung ist schwierig, sodass man Verzweigungen in die Pipeline einbauen muss
Die SQL-Syntax ist unnötig kompliziert
|>-Syntax ist nicht ausdrucksstark und fügt visuelles Rauschen hinzuDer Autor behauptet, "Bedeutung schlägt Syntax", konzentriert sich aber auf Syntaxpräferenzen
effect-ts erlaubt es, sowohl Pipelines als auch imperativen Code zu schreiben