Live data from Hacker News

Working pipe operator today in pure JavaScript

github.com

31–40 of 109 posts

Re: Working pipe operator today in pure JavaScript

#31

In case it might interest anyone, I wrote a similar vanilla JS function last year called Chute. Chute chains methods and function calls using dot-notation. https://github.com/gregabbott/chute

That's Point-free style programming.

https://en.wikipedia.org/wiki/Tacit_programming

Re: Working pipe operator today in pure JavaScript

#32
Neat, but I think that functions already do what we need.

For one thing, the example isn't the most compelling, because you can:

    const greeting = 'hello'.toUpperCase() + '!!!';
or

    const greeting = 'HELLO!!!';
That said, there is already:

    function thrush(initial, ...funcs) {
        return funcs.reduce(
            (current, func) => func(current),
            initial);
    }

    const greeting = thrush('hello', s => s.toUpperCase(), s => s + '!!!');

Re: Working pipe operator today in pure JavaScript

#34
post #33

Pipes are great in environments where "everything is a string" (bash, etc), but do we really need them in javascript? I have yet to see a compelling example.

Pipes are great where you want to chain several operations together. Piping is very common in statically typed functional langauges, where there are lots of different types in play.

Sequences are a common example.

So this:

    xs.map(x => x * 2).filter(x => x > 4).sorted().take(5)
In pipes this might look like:

    xs |> map(x => x * 2) |> filter(x => x > 4) |> sorted() |> take(5)
In functional languages (of the ML variety), convention is to put each operation on its own line:

    xs 
    |> map(x => x * 2) 
    |> filter(x => x > 4) 
    |> sorted() 
    |> take(5)
Note this makes for really nice diffs with the standard Git diff tool!

But why is this better?

Well, suppose the operation you want is not implemented as a method on `xs`. For a long time JavaScript did not offer `flatMap` on arrays.

You'll need to add it somehow, such as on the prototype (nasty) or by wrapping `xs` in another type (overhead, verbose).

With the pipe operator, each operation is just a plain-ol function.

This:

    xs |> f
Is syntactic sugar for:

    f(xs)
This allows us to "extend" `xs` in a manner that can be compiled with zero run-time overhead.

Re: Working pipe operator today in pure JavaScript

#36

Earlier quoted context omitted.

C++ is the reason people have that reaction. The quintessential example in introductory texts for operator overloading is using bit-shift operators to output text. I mean, come on - if that’s your example, don’t complain when people follow suit and get it wrong.

C++ has std::format these days that does a far more sane thing, people are too quick to throw out the baby with the bathwater when it comes to bad things. Some OO is fine, just don't make your architecture or language entirely dependent on it. Same with operator overloading. When it comes to math heavy workloads, you really want a language that supports operator overloading (or have a language full of heavy vector pr…

std::format in C++20 is just for the string manipulation half but you still left shift cout by the resulting string to output text in canonical C++.

C++23 introduced std::print(), which is more or less the modernized printf() C++ probably should have started with and also includes the functionality of std::format(). Unfortunately, it'll be another 10 years before I can actually use it outside of home projects... but at least it's there now!

Re: Working pipe operator today in pure JavaScript

#37
post #33

Pipes are great in environments where "everything is a string" (bash, etc), but do we really need them in javascript? I have yet to see a compelling example.

Pipes are great where you want to chain several operations together. Piping is very common in statically typed functional langauges, where there are lots of different types in play. Sequences are a common example. So this: xs.map(x => x * 2).filter(x => x > 4).sorted().take(5) In pipes this might look like: xs |> map(x => x * 2) |> filter(x => x > 4) |> sorted() |> take(5) In functional languages (of the ML variety),…

if the language or std lib already allows for chaining then pipes aren't as attractive. They're a much nicer alternative when the other answer is nested function calls.

e.g.

So this:

    take(sorted(filter(map(xs, x => x \* 2), x => x > 4)), 5)
To your example:

    xs |> map(x => x \* 2) |> filter(x => x > 4) |> sorted() |> take(5)
is a marked improvement to me. Much easier to read the order of operations and which args belong to which call.

Re: Working pipe operator today in pure JavaScript

#38

Neat, but I think that functions already do what we need. For one thing, the example isn't the most compelling, because you can: const greeting = 'hello'.toUpperCase() + '!!!'; or const greeting = 'HELLO!!!'; That said, there is already: function thrush(initial, ...funcs) { return funcs.reduce( (current, func) => func(current), initial); } const greeting = thrush('hello', s => s.toUpperCase(), s => s + '!!!');

Are any of the cases compelling? Thinking of the actual proposal. It creates some new magic with |> and % just for syntactic sugar.

Re: Working pipe operator today in pure JavaScript

#39
If you're interested in the Ruby language too, check out this PoC gem for an "operator-less" syntax for pipe operations using regular blocks/expressions like every other Ruby DSL.

https://github.com/lendinghome/pipe_operator#-pipe_operator

  "https://api.github.com/repos/ruby/ruby".pipe do
    URI.parse
    Net::HTTP.get
    JSON.parse.fetch("stargazers_count")
    yield_self { |n| "Ruby has #{n} stars" }
    Kernel.puts
  end
  #=> Ruby has 15120 stars

  [9, 64].map(&Math.pipe.sqrt)           #=> [3.0, 8.0]
  [9, 64].map(&Math.pipe.sqrt.to_i.to_s) #=> ["3", "8"]

Re: Working pipe operator today in pure JavaScript

#40
post #33

Pipes are great in environments where "everything is a string" (bash, etc), but do we really need them in javascript? I have yet to see a compelling example.

Pipes are great where you want to chain several operations together. Piping is very common in statically typed functional langauges, where there are lots of different types in play. Sequences are a common example. So this: xs.map(x => x * 2).filter(x => x > 4).sorted().take(5) In pipes this might look like: xs |> map(x => x * 2) |> filter(x => x > 4) |> sorted() |> take(5) In functional languages (of the ML variety),…

First of all, with the actual proposal, wouldnt it actually be like this? with the %.

    xs
      |> map(%, x => x * 2)
      |> filter(%, x => x > 4)
      |> sorted(%)
      |> take(%, 5);
Anything that can currently just chain functions seems like a terrible example because this is perfectly fine:

    xs.map(x => x * 2)
        .filter(x => x > 4)
        .sorted()
        .take(5)
Not just fine but much better. No new operators required and less verbose. Just strictly better. This ignores the fact that sorted and take are not actually array methods, but there are equivalent.

But besides that, I think the better steelman would use methods that dont already exist on the prototype. You can still make it work by adding it to the prototype but... meh. Not that I even liket he proposal in that case.

Post reply on HN