Live data from Hacker News

Pipelining might be my favorite programming language feature

herecomesthemoon.net

81–90 of 360 posts

Re: Pipelining might be my favorite programming language feature

#82
post #49

Earlier quoted context omitted.

> The second approach is open for extension - it allows you to write new functions on old datatypes. I prefer to just generalize the function (make it generic, leverage traits/typeclasses) tbh. > Probably for lack of > weird operators like , , $, or >>= Nope btw. I mean, maybe? I don't know Haskell well enough to say. The answer that I was looking for here is a specific Rust idiosyncrasy. It doesn't allow you to impo…

> It doesn't allow you to import `std::iter::Iterator::collect` on its own. It's an associated function, and needs to be qualified. You probably noticed, but it should become a thing in RFC 3591: https://github.com/rust-lang/rust/issues/134691 So it does kind of work on current nightly: #![feature(import_trait_associated_functions)] use std::iter::Iterator::{filter, map, collect}; fn get_ids2(data: Vec ) -> Vec { col…

Oh, interesting! Thank you, I did not know about that, actually.

Re: Pipelining might be my favorite programming language feature

#83
post #59

Earlier quoted context omitted.

How do you polyfill syntax?

Letting your JS/TS compiler convert it into supported form. Not really a polyfill, but it allows to use new features in the source and still support older targets. This was done a lot when ES6 was new, I remember.

Polyfills are for runtime behavior that can't be replicated with a simple syntax transformation, such as adding new functions to built-in objects like string.prototype contains or the Symbol constructor and prototype or custom elements.

I haven't looked at the member properties bits but I suspect the pipeline syntax just needs the transform to be supported in build tools, rather than adding yet another polyfill.

Re: Pipelining might be my favorite programming language feature

#84

Earlier quoted context omitted.

I feel like Haskell really missed a trick by having $ not go the other way, though it's trivial to make your own symbol that goes the other way.

Haskell has & which goes the other way: users & map validate & catMaybes & mapM persist

I guess I'm showing how long it's been since I was a student of Haskell then. Glad to see the addition!

Re: Pipelining might be my favorite programming language feature

#85
post #42

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.

TI-BASIC is like this with its store operator →. I always liked it.

    10→A
    A+10→C

Re: Pipelining might be my favorite programming language feature

#86

First example doesn't look bad in C++23: auto get_ids(std::span data) { return data | filter(&Widget::alive) | transform(&Widget::id) | to (); }

This is not functionally different from operator<< which std::cout has taught us is a neat trick but generally a bad idea.

Unlike the iostreams shift operators, the ranges pipe operator isn't stateful.

Re: Pipelining might be my favorite programming language feature

#87

Earlier quoted context omitted.

Why, if you don't have to, would you write the functions in reverse order of when they're applied?

Because it makes more sense?

I would wager within a rounding error, all humans have a lifetime of experience in following directions of the form:

1. do the first step in the process

2. then do the next thing

3. followed by a third action

I struggle to think of any context outside of programming, retrosynthesis in chemistry, and some aspects of reverse-Polish notation calculators, where you conceive of the operations/arguments last-to-first. All of which are things typically encountered pretty late in one's educational journey.

Re: Pipelining might be my favorite programming language feature

#88
post #24

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() ```

Yes, a small feature set is important, and adding the functional-style pipe to languages that already have chaining with the dot seems to clutter up the design space. However, dot-chaining has the severe limitation that you can only pass to the first or "this" argument.

Is there any language with a single feature that gives the best of both worlds?

Re: Pipelining might be my favorite programming language feature

#89

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 ) -> Vec { collect(map(filter(map(iter(data), |w| w.toWingding()), |w| w.alive), |w| w.id)) } to fn get_ids(data: Vec ) -> Vec { data.iter() .map(|w| w.toWingding()) .filter(|w| w.alive) .map(|w| w.id) .collect() } The first one would read more easily (and, s…

In my eyes newlines don't solve what I feel to be the issue. Reader needs to recognize reading from left->right to right->left.

Of course this really only matters when you're 25 minutes into critical downtime and a bug is hiding somewhere in these method chains. Anything that is surprising needs to go.

IMHO it would be better to set intermediate variables with dead simple names instead of newlines.

fn get_ids(data: Vec) -> Vec {

    let iter = iter(data);

    let wingdings = map(iter, |w| w.toWingding());

    let alive_wingdings = filter(wingdings, |w| w.alive);

    let ids = map(alive_wingdings, |w| w.id);

    let collected = collect(ids);

    collected

}

Re: Pipelining might be my favorite programming language feature

#90
post #45

Earlier quoted context omitted.

You can add peek steps in pipelines and inspect the in between results. Not really any different from normal function call debugging imo.

Yes, but here's my hot take - what if you didn't have to edit the source code to debug it? Instead of chaining method calls you just assign to a temporary variable. Then you can set breakpoints and inspect variable values like you do normally without editing source. It's not like you lose that much readability from foo(bar(baz(c))) c |> baz |> bar |> foo c.baz().bar().foo() t = c.baz() t = t.bar() t = t.foo()

The Clojure equivalent of `c |> baz |> bar |> foo` are the threading macros:

    (-> c baz bar foo)
But people usually put it on separate lines:

    (-> c
        baz
        bar
        foo)
Post reply on HN