18 Punkte von GN⁺ 2025-04-22 | 11 Kommentare | Auf WhatsApp teilen
  • 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 diff oder git blame lassen sich Änderungen sauber nachvollziehen
  • 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)

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 wie x |> h |> g |> f menschenfreundlicher
  • 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

 
progdesigner 2025-04-23

Das dürfte ein ähnlicher Zusammenhang sein wie bei beliebigem Text,
bei dem Zeilenumbrüche und Einrückungen wichtig für die Lesbarkeit sind.

 
forgotdonkey456 2025-04-23

LINQ ist das Beste!

 
bus710 2025-04-23

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.

 
bus710 2025-04-23

Wenn man darüber nachdenkt, geht das auch in elm.

 
galadbran 2025-04-22

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.

 
bichi 2025-04-22

Gebt uns auch etwas js |> verbeug, verbeug

 
secret3056 2025-04-22

|> ist einfach wunderschön

 
howudoin 2025-04-22

Ist die Syntax, die ich am meisten hasse
Schon wenn der Stacktrace nur ein bisschen verschachtelt ist, ist das Debugging die Hölle

 
cosine20 2025-04-25

Stimmt absolut.

 
GN⁺ 2025-04-22
Hacker-News-Kommentare
  • Der Autor nennt es zwar "pipelining", aber ich denke, der korrekte Begriff ist "method chaining"

    • Vergleich mit einer einfachen Pipeline in Bash: Jede Komponente läuft parallel, und Zwischenergebnisse werden gestreamt
    • In Ruby wird jede Zeile sequenziell verarbeitet, und zwischen den einzelnen Schritten wird jeweils ein vollständiges Array erzeugt
    • Das erschwert das Debugging, deshalb schreibe ich inzwischen eher expliziteren Code
    • Expliziter Code sieht zwar weniger sauber aus, aber Zwischenzustände lassen sich leicht inspizieren
  • Ich persönlich befürworte es, den Funktionsumfang einer Sprache klein zu halten und schnell einen abgeschlossenen Feature-Satz zu erreichen

    • Trotzdem wünschte ich, alle Sprachen würden die |>-Syntax von Elixir übernehmen
  • Lisp-Makros bieten eine allgemeine Lösung, mit der sich nicht nur verkettete Kollektionsoperatoren, sondern auch die Reihenfolge von Aufrufketten festlegen lässt

    • Zum Beispiel kann man (foo (bar (baz x))) als (-> x baz bar foo) schreiben
    • Das funktioniert auch, wenn zusätzliche Argumente vorhanden sind
    • Für Details siehe den Leitfaden zu den Threading-Makros von Clojure
  • 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

    • Partielle Anwendung ist beim Schreiben von Programmen sehr nützlich, und irgendwann werden (Nicht-Haskell-)Sprachen sie als Grundlage der Programmkomposition verwenden
  • Nutzer des tidyverse in R verwenden das bereits

  • Pipelining ist schwer zu debuggen. Ausnahmebehandlung ist schwierig, sodass man Verzweigungen in die Pipeline einbauen muss

    • Pipelines sind nur nützlich, wenn man den Happy Path programmiert
  • Die SQL-Syntax ist unnötig kompliziert

    • SQL ist bereits eine Operatorsprache, aber aus historischen Gründen stark eingeschränkt
    • Wenn man ohnehin neue Syntax zulassen will, könnte man sie auch einfacher schreiben
    • Die |>-Syntax ist nicht ausdrucksstark und fügt visuelles Rauschen hinzu
  • Der Autor behauptet, "Bedeutung schlägt Syntax", konzentriert sich aber auf Syntaxpräferenzen

    • Pipelining wird mit zunehmender Kettenlänge schwerer zu debuggen
    • Er ist Python gegenüber kritisch, nennt aber keine konkreten Gründe
    • Die Definition von "pipelining" bleibt unklar
  • effect-ts erlaubt es, sowohl Pipelines als auch imperativen Code zu schreiben

    • Es gibt Dokumentation zum Schreiben von Pipelines und zur Verwendung von Generatoren
    • Der Großteil der Community scheint den imperativen Stil mit Generatoren zu bevorzugen
    • Debugging und Wartung wirken dadurch einfacher