Skip to content Skip to footer
0 items - $0.00 0

Pipelining might be my favorite programming language feature by Mond_

Pipelining might be my favorite programming language feature by Mond_

Pipelining might be my favorite programming language feature by Mond_

34 Comments

  • Post Author
    SimonDorfman
    Posted April 21, 2025 at 12:41 pm

    The tidyverse folks in R have been using that for a while: https://magrittr.tidyverse.org/reference/pipe.html

  • Post Author
    jaymbo
    Posted April 21, 2025 at 12:42 pm

    This is why I love Scala so much

  • Post Author
    shae
    Posted April 21, 2025 at 12:45 pm

    If Python object methods returned `self` by default instead of `None` you could do this in Python too!

    This is my biggest complaint about Python.

  • Post Author
    mexicocitinluez
    Posted April 21, 2025 at 12:48 pm

    LINQ is easily one of C#'s best features.

  • Post Author
    kordlessagain
    Posted April 21, 2025 at 12:51 pm

    While the author claims "semantics beat syntax every day of the week," the entire article focuses on syntax preferences rather than semantic differences.

    Pipelining can become hard to debug when chains get very long. The author doesn't address how hard it can be to identify which step in a long chain caused an error.

    They do make fun of Python, however. But don't say much about why they don't like it other than showing a low-res photo of a rock with a pipe routed around it.

    Ambiguity about what constitutes "pipelining" is the real issue here. The definition keeps shifting throughout the article. Is it method chaining? Operator overloading? First-class functions? The author uses examples that function very differently.

  • Post Author
    kuon
    Posted April 21, 2025 at 12:52 pm

    That's also why I enjoy elixir a lot.

    The |> operator is really cool.

  • Post Author
    drchickensalad
    Posted April 21, 2025 at 12:52 pm

    I miss F#

  • Post Author
    osigurdson
    Posted April 21, 2025 at 1:01 pm

    C# has had "Pipelining" (aka Linq) for 17 years. I do miss this kind of stuff in Go a little.

  • Post Author
    zelphirkalt
    Posted April 21, 2025 at 1:05 pm

    To one up this: Of course it is even better, if your language allows you to implement proper pipelining with implicit argument passing by yourself. Then the standard language does not need to provide it and assign meaning to some symbols for pipelining. You can decide for yourself what symbols are used and what you find intuitive.

    Pipelining can guide one to write a bit cleaner code, viewing steps of computation as such, and not as modifications of global state. It forces one to make each step return a result, write proper functions. I like proper pipelining a lot.

  • Post Author
    mrkeen
    Posted April 21, 2025 at 1:07 pm

      data.iter()
          .filter(|w| w.alive)
          .map(|w| w.id)
          .collect()
    
      collect(map(filter(iter(data), |w| w.alive), |w| w.id))
    

    The second approach is open for extension – it allows you to write new functions on old datatypes.

    > Quick challenge for the curious Rustacean, can you explain why we cannot rewrite the above code like this, even if we import all of the symbols?

    Probably for lack of

    > weird operators like <$>, <*>, $, or >>=

  • Post Author
    epolanski
    Posted April 21, 2025 at 1:11 pm

    I personally like how effect-ts allows you to write both pipelines or imperative code to express the very same things.

    Building pipelines:

    https://effect.website/docs/getting-started/building-pipelin…

    Using generators:

    https://effect.website/docs/getting-started/using-generators…

    Having both options is great (at the beginning effect had only pipe-based pipelines), after years of writing effect I'm convinced that most of the time you'd rather write and read imperative code than pipelines which definitely have their place in code bases.

    In fact most of the community, at large, converged at using imperative-style generators over pipelines and having onboarded many devs and having seen many long-time pipeliners converging to classical imperative control flow seems to confirm both debugging and maintenance seem easier.

  • Post Author
    bnchrch
    Posted April 21, 2025 at 1:14 pm

    I'm personally someone who advocates for languages to keep their feature set small and shoot to achieve a finished feature set quickly.

    However.

    I would be lying if I didn't secretly wish that all languages adopted the `|>` syntax from Elixir.

    “`

    params

    |> Map.get("user")

    |> create_user()

    |> notify_admin()

    “`

  • Post Author
    chewbacha
    Posted April 21, 2025 at 1:21 pm

    Is this pipelining or the builder pattern?

  • Post Author
    cutler
    Posted April 21, 2025 at 1:23 pm

    Clojure has pipeline functions -> and ->> without resorting to OO dot syntax.

  • Post Author
    amelius
    Posted April 21, 2025 at 1:31 pm

    Am I the only one who thinks yuck?

    Instead of writing: a().b().c().d(), it's much nicer to write: d(c(b(a()))), or perhaps (d ∘ c ∘ b ∘ a)().

  • Post Author
    1899-12-30
    Posted April 21, 2025 at 1:36 pm

    You can somewhat achieve a pipelined like system in sql by breaking down your steps into multiple CTEs. YMMV on the performance though.

  • Post Author
    singularity2001
    Posted April 21, 2025 at 1:39 pm

    I tried to convince the julia authors to make a.b(c) synonymous to b(a,c) like in nim (for similar reasons as in the article). They didn't like it.

  • Post Author
    dapperdrake
    Posted April 21, 2025 at 1:47 pm

    Pipelining in software is covered by Richard C. Waters (1989a, 1989b). Wrangles this library to work with JavaScript. Incredibly effective. Much faster at writing and composing code. And this code executes much faster.

    https://dspace.mit.edu/handle/1721.1/6035

    https://dspace.mit.edu/handle/1721.1/6031

    https://dapperdrake.neocities.org/faster-loops-javascript.ht…

  • Post Author
    blindseer
    Posted April 21, 2025 at 1:48 pm

    This article is great, and really distills why the ergonomics of Rust is so great and why languages like Julia are so awful in practice.

  • Post Author
    hliyan
    Posted April 21, 2025 at 1:49 pm

    I always wondered how programming would be if we hadn't designed the assignment operator to be consistent with mathematics, and instead had it go LHS -> RHS, i.e. you perform the operation and then decide its destination, much like Unix pipes.

  • Post Author
    TrianguloY
    Posted April 21, 2025 at 2:00 pm

    Kotlin sort of have it with let (and run)

        a().let{ b(it) }.let{ c(it) }

  • Post Author
    wslh
    Posted April 21, 2025 at 2:30 pm

    I also like a syntax that includes pipelining parallelization, for example:

    A

    .B

    .C

      || D
    
      || E

  • Post Author
    tantalor
    Posted April 21, 2025 at 2:34 pm

    > allows you to omit a single argument from your parameter list, by instead passing the previous value

    I have no idea what this is trying to say, or what it has to do with the rest of the article.

  • Post Author
    0xf00ff00f
    Posted April 21, 2025 at 2:36 pm

    First example doesn't look bad in C++23:

        auto get_ids(std::span<const Widget> data)
        {
            return data
                | filter(&Widget::alive)
                | transform(&Widget::id)
                | to<std::vector>();
        }

  • Post Author
    RHSeeger
    Posted April 21, 2025 at 2:44 pm

    I feel like, at least in some cases, the article is going out of its way to make the "undesired" look worse than it needs to be. Compairing

        fn get_ids(data: Vec<Widget>) -> Vec<Id> {
            collect(map(filter(map(iter(data), |w| w.toWingding()), |w| w.alive), |w| w.id))
        }
    

    to

        fn get_ids(data: Vec<Widget>) -> Vec<Id> {
            data.iter()
                .map(|w| w.toWingding())
                .filter(|w| w.alive)
                .map(|w| w.id)
                .collect()
        }
    

    The first one would read more easily (and, since it called out, diff better)

        fn get_ids(data: Vec<Widget>) -> Vec<Id> {
            collect(
                map(
                    filter(
                        map(iter(data), |w| w.toWingding()), |w| w.alive), |w| w.id))
        }
    

    Admittedly, the chaining is still better. But a fair number of the article's complaints are about the lack of newlines being used; not about chaining itself.

  • Post Author
    gus_leonel
    Posted April 21, 2025 at 2:54 pm

    [dead]

  • Post Author
    guerrilla
    Posted April 21, 2025 at 3:30 pm

    This is just super basic functional programming. Seems like we're taking the long way around…

  • Post Author
    duped
    Posted April 21, 2025 at 3:39 pm

    A pipeline operator is just partial application with less power. You should be able to bind any number of arguments to any places in order to create a new function and "pipe" its output(s) to any other number of functions.

    One day, we'll (re)discover that partial application is actually incredibly useful for writing programs and (non-Haskell) languages will start with it as the primitive for composing programs instead of finding out that it would be nice later, and bolting on a restricted subset of the feature.

  • Post Author
    weinzierl
    Posted April 21, 2025 at 3:55 pm

    I suffer from (what I call) bracket claustrophobia. Whenever brackets get nested too deep I makes me uncomfortable. But I fully realize that there are people who are the complete opposite. Lisp programmers are apparently as claustrophil as cats and spelunkers.

  • Post Author
    pxc
    Posted April 21, 2025 at 4:06 pm

    Maybe it's because I love the Unix shell environment so much, but I also really love this style. I try to make good use of it in every language I write code in, and I think it helps make my control flow very simple. With lots of pipelines, and few conditionals or loops, everything becomes very easy to follow.

  • Post Author
    taeric
    Posted April 21, 2025 at 4:08 pm

    A thing I really like about pipelines in shell scripts, is all of the buffering and threading implied by them. Semantically, you can see what command is producing output, and what command is consuming it. With some idea of how the CPU will be split by them.

    This is far different than the pattern described in the article, though. Small shame they have come to have the same name. I can see how both work with the metaphor; such that I can't really complain. The "pass a single parameter" along is far less attractive to me, though.

  • Post Author
    tpoacher
    Posted April 21, 2025 at 4:12 pm

    pipelines are great IF you can easily debug them as easily as temp variable assignments

    … looking at you R and tidyverse hell.

  • Post Author
    layer8
    Posted April 21, 2025 at 4:12 pm

    The one thing that I don’t like about pipelining (whether using a pipe operator or method chaining), is that assigning the result to a variable goes in the wrong direction, so to speak. There should be an equivalent of the shell’s `>` for piping into a variable as the final step. Of course, if the variable is being declared at the same time, whatever the concrete syntax is would still require some getting used to, being “backwards” compared to regular assignment/initialization.

  • Post Author
    bluSCALE4
    Posted April 21, 2025 at 4:17 pm

    Same. The sad part is that pipelining seems to be something AI is really good at so I'm finding myself writing less of it.

Leave a comment

In the Shadows of Innovation”

© 2025 HackTech.info. All Rights Reserved.

Sign Up to Our Newsletter

Be the first to know the latest updates

Whoops, you're not connected to Mailchimp. You need to enter a valid Mailchimp API key.