The biggest issue with this video is that it uses examples that don’t show the advantage of using a pipeline. It would have been better to use something that was a series of functions instead of a chain of methods, as this proposal is designed to replace nested functions. If that had been shown, most people would go for hack, since it would use a character to pipe the output into the next function and would allow functions with multiple parameters to specify which one to provide. An example: subtract(%, 8) vs subtract(8, %) As seen above, the function would be the same but the result would change due to the order of the values. So, this is designed to turn something that is a set of functions into something that looks similar to chained methods, which would change the following: trim(lowercase(“ Hello “)); To this: lowercase(“ Hello “) |> trim(%) The proposed syntax isn’t exactly designed for already existing chained methods, but rather to bind multiple functions into something that resembles using chained methods.
Thank you for the example. I was doubtful of the proposal’s usefulness while watching the video, but I can definitely see myself using it for nested functions.
but nesting too much functions is a bad pattern. So if you make the syntax more cool it would just make the ppl who overuse it. to overuse it even more
It's worth noting that while the examples show array iteration, that's not the important part of the proposal. The actual interesting thing that's happening is function composition. In the F# syntax, it can be point-free, and the previous call's return value is 'piped' to the next function's argument implicitly as the first positional / unary argument. In the Hack syntax, there is no need for it to be a unary function, as the % lets you use the argument in any positional location, but loses the point-free goodness. The F# syntax is more elegant but more restrictive - in real-world scenarios you will probably end up doing a lot of partial application to make the most of it. The Hack syntax, though not as elegant, might be the more practical thing to use.
I think the first part is right on. "The F# syntax, it can be point-free, and the previous call's return value is 'piped' to the next function's argument implicitly as the first positional / unary argument" " the Hack syntax ... loses the point-free goodness" "The F# syntax is more elegant but more restrictive" And when comparing the 2 it seems like "The Hack syntax, though not as elegant, might be the more practical thing to use." until you realize that the hack syntax, taken by itself, without comparison to the F# syntax is a useless feature. It is the same "feature" as reassigning a `let` variable on each new line. There is no benefit to it when looking at it alone, without considering the F# syntax, or at least no benefit that would offset the cost of adding new syntax features and having it break in runtimes that don't yet support the feature. So, I could understand wanting new syntax to allow for a more functional, point-free, style of programming. I don't need it, but I can understand the want for it. But the Hack syntax is just ...well an almost nothing feature just for adding a feature-sake ...in my opinion.
Hey! Love this video. I worked on this proposal quite a bit over the years (as a community member; I'm not on TC39). Couple of quick notes: 1. As you mention, the F# version has been rejected multiple times, and the pipeline operator advanced to stage 2 as the Hack proposal. 2. There are a few reasons why F# has been rejected a few times: a. the runtime cost of creating new closures. b. the inability of the F# proposal to support await mid-pipeline without workarounds (part of the proposal was a "unary await" but this always felt like a bit of wart) c. the general impedance mismatch between unary-functions (and its cousin, currying) and the broader JS ecosystem. 3. The Hack version is actually at its most powerful not when translated method chaining but in unwinding nested expressions. People look at it as a cousin to method chaining because it looks syntactically similar (and admittedly, some of the initial motivation was "importable methods" a la RxJS) but that's not actually where it's at its most useful, and I would love to see more exploration of the Hack pipe that dives into this use case. Thanks for covering this! I think the proposal has stalled out a bit (for reasons unrelated to the F#/Hack split) so I hope this can help breathe some life into it again.
It's funny having a functional concept being imported from a dialect of PHP (Hack). I'm partial to the Hack version, since the F# ends up having more overhead because of the extra closures for naturally chained methods (two new closures per line instead of one). I'd be happy with any of those, though. Also, I think a better case for the pipeline would be for expressions like f(g(h(a))), comparing it to the flow function from Ramda/Lodash.
@Efecretion you disagree? Functions were the way to go before they introduced classes to have some oop or other concepts. There is a reason why "this" is such a weird concept on JS
Good to see this. This just differentiate method chaining and functional chaining. This force freshers to use pure functions. People who knows about functional programming really loves this. This will really improve code quality. There is no need to complicate your code with bind, call, apply, OOP concepts and fight with 'this' keywords. Edit: yes it does not force to use pure functions but the code with pipe operator suggest freshers to use pure functions.
I love functional programming. I use it everyday using the compose function. It's a little verbose, but I'm glad that we have this, and I can remove that compose function. I do a question for you. If I want to pipe a number into a series of validations (input number -> output boolean), I cannot compose these functions because the output needs to be number going into the input of another function. One way to do it, it to store the functions in an array and use reduce, but is there another way to compose predicate functions for a single input?
Is there anything that forces you to use pure functions? What prevents you from doing impure stuff such as using global state, mutating x, writing to global state etc.? At 3:53 he's using a console.log in there, which is clearly impure.
@@PS-dp8yg , to apply series of validation or for any High level scenario the value can be object with only values. Your object can be { value: 5, isEven: null, isThresholdValue: null, isAllValid: null }. You can call functions like 5 |> NumValidate.init |> NumValidate.checkIsEven |> NumValidate.checkIsThresholdValue |> NumValidate.checkIsAllValid. This is what you expect?
I use the Pipeline Operator in R a lot. I hope they will move forward with this proposal as soon as possible. But from what I see, there are way too many ongoing discussions about which syntax to use and which features to include or exclude.
The pipeline operator is awesome in Elixir because the language is made with it in mind. These two JavaScript proposals need better examples, though. Comparing the pipeline operator to method chaining limits what you can do with the pipeline. If it’s a series of custom functions you’re chaining together, it makes more sense.
I prefer the hack version. The need to use unary functions in the F# version sucks. It basically means you'll always be using anonymous functions as there's no way to pass in additional context making the pipeline functions far less reusable. I wish it worked more like elixir where the piped in value is passed as the first argument to the function. Then you can make a set of composable functions around a given domain and pieces them together as needed. The hack version isn't bad though. It gets close to that and actually might be a little more flexible.
Wow, end of video gold... where have subexpressions that return the last argument been? Not in my brain 😄 Thanks! I am not sure how often we will use the pipes outside of really needing it for some reason. It also seems simpler to just store x and do the manual calls and reassign with it, and probably still uses same memory. It is a nifty feature though and I'm always glad to see them added.
That can be achieved by wrapping all the logic in a single reduce statement. This video serves to illustrate the new ways you can compose chained functions with this new spec. Piping used to require verbose code which always returned `this` and was inflexible around introducing unrelated functions into the mix. Pretty neat proposal!
@BikerYen Oh yeah, I see what you mean. I was thinking in terms of arrays, and I didn't think this was super useful. But making reduce easier to write would be game changing. I did some googling and found out that it actually exists as iterator helpers and it's in stage 4 right now
This title is arguably disingenuous. It's not a "New javascript operator" - this proposal has been around for ages (the repo was last updated 4 years ago but it's much older than that) and there is literally nothing that says this operator will ever make it into javascript. It's also arguably bad to suggest e.g. newbies(which are likely the majority of your audience) start creating projects with babel transforms and whatnot.
This just differentiate object chaining and functional chaining. This suggests freshers to use pure functions. People who knows about functional programming really loves this. This will really improve code maintainability. You no more required to complicate your code with bind, call, apply and fight with 'this' keywords.
@@vetrivendhan6122 3:53 - But how is for instance console logging pure? That's clearly a side-effect. What stops me from using global state, mutating x, mutating global state etc.?
About @3:18: You can also add a console log to former method chaining as well. const multiplyAndSumBigNumbers = numbers .filter(v => v > 10) .map(v => (console.log('current item:', v), v * 2)) .reduce((acc, v) => acc + v, 0) OR const multiplyAndSumBigNumbers = numbers .filter(v => v > 10) .map(v => { console.log('current item:', v); return v * 2; }) .reduce((acc, v) => acc + v, 0) Main difference between array method chaining vs pipeline is: In array method, for the first step (first array method call) it goes through entire array then switches to the next step, In Pipeline, for the first item it goes through all the steps then switches to the second item etc...
No that is how iterator methods/lazy evaluation works, pipes kinda do something similar to the '.' chaining, the only difference is the '.' binds the variable to the 'this' of the called function and the '|>' just fills the first argument or the '%' variable.
Hmm... I don't particularly like it without built in functions for the operations. I mean yeah, console.log in the middle of the chain is nice, but you could do that before with any other method like .map, just return x at the end to not change the array. For now looks like more characters for less functionality. It would be nice for some things, if you had to apply the same transformation in multiple places and you could easily extract it to a separate function for example, then you wouldn't need to write `|> (x) => fn(x)`, you could just write `|> fn`, which I think is the intended use case. But 90% of my maps/filters/reduces are one-time things. Maybe curried functions would be beneficial too for this, make a map function that returns an unary function, then you could skip the (x)=>x.map(fn), could just do map(fn). Seems bad for performance though. Not that the standard functions are good since they duplicate the array each time. Overall, I'm on the fence. I thought I want this, but after seeing live code I'm a bit underwhelmed.
While .map does work for most situations, it would be nice to have a way to access the entire array in the middle of the chain - especially for quick debugging. With map, you just see an element at a time - which can be clunky if you are concerned with the entire thing (for example, you want to count what passes through a filter). I'm not sure how useful it would be, but I routinely find myself wishing this existed. Honestly, just having a .pipe() added to the array object would be great for me.
It's a bit semantically different. If you console.log in the middle of the map it will print each element x, instead of the array of xs. That said, I don't really see the value either
@@bobthemagicmoose a terrible hack for that is: arr.map((x, index, array)=>{ if ( index===0) console.log(array) return x }) You get the entire array as the third parameter in map. I never found a use case for it outside of debugging, and even then it's pretty rare. Of course keep in mind this will be pretty slow for large arrays, as it still iterates over the whole thing. Honestly I'd just want the .forEach to return the original array instead of void and everything would be golden, you could just put it in the middle without modifying anything and without allocations. And it's not like that would have any impact on existing things or on performance (I don't think at least)
will probably depend on the V8 implementation of the methods used in these. Right now I think it just copies the entire thing from one pipe to another. Could be probably massively improved, but would require lots of work from the V8 engineers.
Hack can be anything. Jack just used %. My issues is not the % per sey, but the fact it is using something that should have never been put into the compilation to begin with. var x = (1, 2, 3); // x == 3 // should just throw an error or x equals 1, 2 or 3 (like a random number)
You could wrap a console log in a forEach, or a map, or add a new function to the Array prototype as some suggested. But if you're suggesting you can just put a `.` in front of the console log and have it chain, that would not work.
It would have been better to not use chained functions since chaining like this is already nice. It’s when the function is not on the output but is just a normal function that this is really nice. When c(b(a(result))) becomes result |> a |> b |> c that it’s really clean
I vote hack pipe, the reason why is other languages that use unary operators build functions to be data first inherently because it's a standard. But Javascript isn't those langauges, having the flexibility of the hack pipe mines you're more easily able to plug into different types of functions. In general when you're doing functional programming it's pretty common that you WONT use a method off an array like this and instead your code would look like // unary would look like import {filter} from "remeda" arr |> filter(n => n.length > 5) |> n => slice(str, 0, 5) // hack would look like arr |> filter(%, n => n.length) |> slice(%, 0, 5) which to me adds a ton more flexibility and matches the js ecosystem more. Otherwise you're going to have to do stuff like make generic curry functions and things of the sort
Yes. The output of each piece of the pipeline can be a different data type. So the expression can start with a string, split it into an array, reformat it into an object, then convert that object to a date, just as a crazy example.
TIL about the JS comma operator. 5:48 Of course I knew it could be used in the different parts of the for-loop signature, when multiple variables are in play, but I did not know it had this return behavior. THANK YOU.
function chaining doesn't always work. I've run into a couple of instances where I had to break the chain into separate lines of code (if I recall, one of them was regarding date manipulation). At the same time, Unless pipelining is tangible faster/more efficient, I'm not sure why there's even a need for it.
I mean I get it: it makes nested function calls cleaner. But the inherit complexity this brings to the table is not something I see as net positive. TypeScript is already getting way carried away with its features and starting to smell like C++. And many people choose pure JS because of that. Now we’re making JS more convoluted too? Imagine trying to maintain a simple and readable code base and a junior dev comes all hyped up with this stuff.
It is worrying for me to overload the `%` modulo operator as a $in variable. I wonder why # or @ was not chosen? I know of now function for these, yet. Other than that, the hack syntax seems nice.
I really don't know what's the benefit? just adding more ways to do the same things and making it harder for devs to work on existing projects. That's why I'm loving Go atm and getting away slowly from js.
If you come from functional languages and see the functional core of the javascript language it's consequential to have this missing feature. Pipe operators indicate data flow. It's a hint. If your background is an object oriented language you will see Javascript as an awkward cousin and introducing pipes make him more awkward.
Comma chain... great to learn something new. I had no idea about that. As far as the syntax goes, I prefer the fSharp syntax. Wondering if there is a 'compose' version of this in the works too. I have been using RamdaJS for 4 years now and love it. Also wondering about async await within the context of the pipelines.
I don't understand, explain me, what is the difference between this and using .map(v => (console.log(v), v)) ? I'd prefer if they introduce a .tap method for side effects processing similar to rxjs.
I don't get it. Isn't `x |> fn` the same as fn(x) ? Is the goal just to reduce nesting? I feel like a helper function like `myApply(array, ...funcs)` would do just as good a job. The overhead would be negligible compared to the Array allocations.
Thank you for the video! While watching, I got curious whether this approach would be optimized differently on various engines. Since currently, for small arrays, it's still convenient to use a chain of methods, but for large arrays, it's better to use a single loop with multiple conditions
Thx, I am a big fun of pipeline operator. My vote if F# version, because that is really strict to use. 3 years ago I am using proposal pipeline operator in real world react app and that is so usefull in many cases. But this days I turn to no compiled HTML/JS direction, so I am waiting to pipeline operator are passing the proposal state.
I have been trying to get the F# version into PHP for a while. I'm actually prepping a new attempt right now that I hope I can squeeze into the next release. (If voters go for it.) I have never liked the Hack version, even if it is a PHP fork. I'd much rather work with pure functions than have to bend everything into an expression. I want to pre build the steps in advance.
The operator could be used with any data type and is meant to flatten nested function calls. The example with arrays is a poor one because arrays is actually a data type in JavaScript that already allows you to chain function calls. But would the code look like without Array prototype functions? You'd get something like reduce(map(filter(numbers))) But with chaining you get numbers.filter().map().reduce() However, what if you're not dealing with arrays? For example, what if you want to pass the result of reduce into another function? fun(numbers.filter().map().reduce()) Suddenly, Array's prototype functions don't help us. What we'd like to do is numbers.filter().map().reduce() |> fun
Why do you want this? In this example it's more characters, less readable and the character '|' is pain in the ass to write with some keyboard layouts.
@@jonikyronlahti I and most of the people i know are already using pipe() functions when writing js/ts. That's exactly this but without a special syntax (so more ugly and less general). I guess you'd want to get a different perspective on that, try writing some elixir or F# in your spare time
@ I feel like the pipe syntax is already pretty nice in libraries like rxjs or effect. Adding this to the language itself is a lot of work for little gain imo. Every browser engine has to implement it and then Typescript has to add it, etc. I would much rather see something like patter matching in JS than this. Ofc it's not a "either/or", but there should be some prioritization.
@ Well effect has function composition for pattern matching too. I don't think there is a bottleneck in adding syntax support considering that a babel plugin implementation of these pipes already exists. What really stops us from getting this good stuff is how long looking at proposals takes the committee. Implementation is barely an issue.
Thanks for the video, I think the F# approach is a lot more readable. Personally, I would have preferred another key than the "|" it's just annoying to type!
That's exciting. Looking forward to this implementation... in a few years? I *think* I like the hack version more - but before I settle on either or, I'd need to understand and compare the differences between F# and Hack, other than what you've just shown here.
Obviously if we get this operator, we will need new versions of filter, map and reduce, so we don't have to wrap this in yet another silly function closure.
If you could bug TC39 to pull their finger out and get this sorted that would be great. They have been sitting on this for years it's really nice syntactic sugar
A lot of confused people in the comments, I think a look at promise chaining would have been a better example. Promise chaining is basically the F# version already and has the issue of not being flexible with argument order. While the hack version looks more "magical"/"scary", I think it is the more practical option.
I think it can make code easier to understand and write, especially for less experienced programmers (not me btw). I can't tell you how many times I've seen a sort of inverse pattern in code where you have to work from the 'inside out', eg function3(function2(function1('value'))) or like was alluded to in the video tn1 = function1('value'); tn2 = function2(tn1); /* etc */ (Edit: I am wrong about the following statement )The latter example being especially egregious in larger web applications because generally speaking declaring unnecessary variables should be avoided for performance reasons. Its nice for little one-off statements that don't need to be refactored into an explicit function.
@@eqprog Those intermediate arrays still exist though, they are just unnamed temporaries that are tagged for garbage collection. The same thing would happen with all of the named temporaries on the way to building the final array that's retained because there would most likely be other references to it.
@@jherr thanks for the correction. I updated my comment to reflect that it’s inaccurate in case someone else reads it and doesn’t read your follow up. I sort of figured that the pipeline operator would work like this under the hood actually. In your opinion, would the former example be faster or are they essentially the same?
@@eqprog Hmmm, just ranch a benchmark and the chaining, at least in the case that I tested, was marginally faster than the broken out version. There are probably some optimizations the v8 engine can make to reduce the amount of checking required in the chaining implementation.
Second one seems cleaner. I think it would be easier to rewrite existing chains into the new way. It is clearer that something is a method used on array instead of function that array is passed into. I didn’t know about that about parenthesis and expressions.
I’m surprised the F# syntax has been rejected, I find that one easier to understand by just looking at it and feel like any developer can figure it out The hack syntax introduces % which doesn’t make sense to me as a JS dev but maybe there’s something I’m missing
Isn't this just syntactic sugar for `[].map()`? You could have just written: `.map((x) => { console.log(x); return x;})`. Less characters and easier to read.
The biggest issue with this video is that it uses examples that don’t show the advantage of using a pipeline. It would have been better to use something that was a series of functions instead of a chain of methods, as this proposal is designed to replace nested functions. If that had been shown, most people would go for hack, since it would use a character to pipe the output into the next function and would allow functions with multiple parameters to specify which one to provide.
An example: subtract(%, 8) vs subtract(8, %)
As seen above, the function would be the same but the result would change due to the order of the values. So, this is designed to turn something that is a set of functions into something that looks similar to chained methods, which would change the following:
trim(lowercase(“ Hello “));
To this:
lowercase(“ Hello “)
|> trim(%)
The proposed syntax isn’t exactly designed for already existing chained methods, but rather to bind multiple functions into something that resembles using chained methods.
Thank you for the example. I was doubtful of the proposal’s usefulness while watching the video, but I can definitely see myself using it for nested functions.
but nesting too much functions is a bad pattern. So if you make the syntax more cool it would just make the ppl who overuse it. to overuse it even more
The F# version, to me, seems vastly superior. It doesn't rely on obscure or unique syntax other than the new operator itself.
This
This ^^^
@@cintron3d ^^ Basically F# seems to be more JS native syntax and feels more natural, no idea why they have rejected it multiple times lol
Also, it just uses functions...which JS is good at. Fewer magical characters, the better.
except it is so verbose it would have been easier to store the intermidiary array in a variable, log it, and use the original method chaining instead.
Wow! I didn't know about that comma chain syntax when two or more values are in parenthesis. Thank you.
If you look at minified output you'll see it a lot, at least it used to be very common.
Finally I can debug in production
alternatively, he could have done `console.log(%) || %` which is a syntax that more people are familar with.
@@ithinkimhipster502 ts won't like that.
It's worth noting that while the examples show array iteration, that's not the important part of the proposal. The actual interesting thing that's happening is function composition. In the F# syntax, it can be point-free, and the previous call's return value is 'piped' to the next function's argument implicitly as the first positional / unary argument. In the Hack syntax, there is no need for it to be a unary function, as the % lets you use the argument in any positional location, but loses the point-free goodness. The F# syntax is more elegant but more restrictive - in real-world scenarios you will probably end up doing a lot of partial application to make the most of it. The Hack syntax, though not as elegant, might be the more practical thing to use.
I think the first part is right on.
"The F# syntax, it can be point-free, and the previous call's return value is 'piped' to the next function's argument implicitly as the first positional / unary argument"
" the Hack syntax ... loses the point-free goodness"
"The F# syntax is more elegant but more restrictive"
And when comparing the 2 it seems like "The Hack syntax, though not as elegant, might be the more practical thing to use." until you realize that the hack syntax, taken by itself, without comparison to the F# syntax is a useless feature.
It is the same "feature" as reassigning a `let` variable on each new line.
There is no benefit to it when looking at it alone, without considering the F# syntax, or at least no benefit that would offset the cost of adding new syntax features and having it break in runtimes that don't yet support the feature.
So, I could understand wanting new syntax to allow for a more functional, point-free, style of programming. I don't need it, but I can understand the want for it.
But the Hack syntax is just ...well an almost nothing feature just for adding a feature-sake ...in my opinion.
Yup, I definitely agree that the Hack syntax is not elegant. I guess this is why this proposal hasn't progressed. :)
Well said. The point free scenarios are clean and readable. I think Jack is great but didn’t explain this one too well.
Yea, 'simple vs flexible' has been part of the debate for the past FIVE years.
Thank you for the explanation.
That comma chain syntax is very interesting. It's been a while since I learnt something new about JavaScript, thanks Jack!
Hey! Love this video. I worked on this proposal quite a bit over the years (as a community member; I'm not on TC39). Couple of quick notes:
1. As you mention, the F# version has been rejected multiple times, and the pipeline operator advanced to stage 2 as the Hack proposal.
2. There are a few reasons why F# has been rejected a few times: a. the runtime cost of creating new closures. b. the inability of the F# proposal to support await mid-pipeline without workarounds (part of the proposal was a "unary await" but this always felt like a bit of wart) c. the general impedance mismatch between unary-functions (and its cousin, currying) and the broader JS ecosystem.
3. The Hack version is actually at its most powerful not when translated method chaining but in unwinding nested expressions. People look at it as a cousin to method chaining because it looks syntactically similar (and admittedly, some of the initial motivation was "importable methods" a la RxJS) but that's not actually where it's at its most useful, and I would love to see more exploration of the Hack pipe that dives into this use case.
Thanks for covering this! I think the proposal has stalled out a bit (for reasons unrelated to the F#/Hack split) so I hope this can help breathe some life into it again.
Personally I like the F# version. It's a bit more clear to me that it's "just a function" whereas the "%" feels a little "magical" to me.
The proposal hasn't decided on what the topic character will be so for this example, he chose his own.
@@ithinkimhipster502The point is that any specific topic character feels magical, no matter what they end up choosing
There is no way they will use '%' because '%' is already an operator.
It's funny having a functional concept being imported from a dialect of PHP (Hack).
I'm partial to the Hack version, since the F# ends up having more overhead because of the extra closures for naturally chained methods (two new closures per line instead of one). I'd be happy with any of those, though.
Also, I think a better case for the pipeline would be for expressions like f(g(h(a))), comparing it to the flow function from Ramda/Lodash.
Woah the (0,1,2) thing is so damn helpful. I didnt even know that was possible. Its just so damn useful
Pipeline is great, especially for something that you CAN'T already method chain. It's meant to make nested function calls better
Clear code is better than clever code
Clean Code even better.
The F# version looks more fitting for JS as JS is all about functions.
I would love to see this as it kinda reminds me of the pipe operator from rxjs
JS is all about functions?!? WHAT ?
@Efecretion you disagree? Functions were the way to go before they introduced classes to have some oop or other concepts. There is a reason why "this" is such a weird concept on JS
@@karamuto1565 js is all about objects
Good to see this. This just differentiate method chaining and functional chaining. This force freshers to use pure functions. People who knows about functional programming really loves this. This will really improve code quality. There is no need to complicate your code with bind, call, apply, OOP concepts and fight with 'this' keywords.
Edit: yes it does not force to use pure functions but the code with pipe operator suggest freshers to use pure functions.
I love functional programming. I use it everyday using the compose function. It's a little verbose, but I'm glad that we have this, and I can remove that compose function. I do a question for you. If I want to pipe a number into a series of validations (input number -> output boolean), I cannot compose these functions because the output needs to be number going into the input of another function. One way to do it, it to store the functions in an array and use reduce, but is there another way to compose predicate functions for a single input?
Is there anything that forces you to use pure functions? What prevents you from doing impure stuff such as using global state, mutating x, writing to global state etc.? At 3:53 he's using a console.log in there, which is clearly impure.
Yeah, this doesn't enforce purity; it just encourages the functional style.
@@PS-dp8yg , to apply series of validation or for any High level scenario the value can be object with only values. Your object can be { value: 5, isEven: null, isThresholdValue: null, isAllValid: null }. You can call functions like 5 |> NumValidate.init |> NumValidate.checkIsEven |> NumValidate.checkIsThresholdValue |>
NumValidate.checkIsAllValid. This is what you expect?
@@bluecup25 my bad. It not force to use pure functions. But it will suggest to use pure functions and freshers will avoid use of class in JavaScript.
I use the Pipeline Operator in R a lot. I hope they will move forward with this proposal as soon as possible. But from what I see, there are way too many ongoing discussions about which syntax to use and which features to include or exclude.
Hi Jack, great to see you. Very informative. Thanks.
Wait, is that like Elixir pipe operator?
I think this has been a proposal as long as I can remember.
The pipeline operator is awesome in Elixir because the language is made with it in mind. These two JavaScript proposals need better examples, though. Comparing the pipeline operator to method chaining limits what you can do with the pipeline. If it’s a series of custom functions you’re chaining together, it makes more sense.
We got pipeline operator in javascript before gta 6 🗣️🗣️🗣️🔥🔥🔥
I prefer the hack version. The need to use unary functions in the F# version sucks. It basically means you'll always be using anonymous functions as there's no way to pass in additional context making the pipeline functions far less reusable. I wish it worked more like elixir where the piped in value is passed as the first argument to the function. Then you can make a set of composable functions around a given domain and pieces them together as needed. The hack version isn't bad though. It gets close to that and actually might be a little more flexible.
Wow, end of video gold... where have subexpressions that return the last argument been? Not in my brain 😄 Thanks!
I am not sure how often we will use the pipes outside of really needing it for some reason. It also seems simpler to just store x and do the manual calls and reassign with it, and probably still uses same memory.
It is a nifty feature though and I'm always glad to see them added.
Id like to see something like this that does it all in one loop
That can be achieved by wrapping all the logic in a single reduce statement. This video serves to illustrate the new ways you can compose chained functions with this new spec. Piping used to require verbose code which always returned `this` and was inflexible around introducing unrelated functions into the mix. Pretty neat proposal!
@BikerYen Oh yeah, I see what you mean. I was thinking in terms of arrays, and I didn't think this was super useful. But making reduce easier to write would be game changing.
I did some googling and found out that it actually exists as iterator helpers and it's in stage 4 right now
@@BikerYen It makes sense that this for things like chaining libraries would be really useful.
Pipe and pattern matching are the two i dream to come to JavaScript
Good to see a functional programmer here.
I think pattern matching would have been more useful as opposed to the pipe operator for me personally
This title is arguably disingenuous. It's not a "New javascript operator" - this proposal has been around for ages (the repo was last updated 4 years ago but it's much older than that) and there is literally nothing that says this operator will ever make it into javascript. It's also arguably bad to suggest e.g. newbies(which are likely the majority of your audience) start creating projects with babel transforms and whatnot.
this is why I like go's approach to keep the language simple. looking at the code with the pipeline syntax is not intuitive at all.
Am I the only one who does not get the hype for this?
Readability jest very bad imo
This just differentiate object chaining and functional chaining. This suggests freshers to use pure functions. People who knows about functional programming really loves this. This will really improve code maintainability. You no more required to complicate your code with bind, call, apply and fight with 'this' keywords.
yes, you're the only one
@@vetrivendhan6122 3:53 - But how is for instance console logging pure? That's clearly a side-effect. What stops me from using global state, mutating x, mutating global state etc.?
I'm with you. It seems like extra complexity for no real reason. Just make your own array "tap" function.
About @3:18:
You can also add a console log to former method chaining as well.
const multiplyAndSumBigNumbers = numbers
.filter(v => v > 10)
.map(v => (console.log('current item:', v), v * 2))
.reduce((acc, v) => acc + v, 0)
OR
const multiplyAndSumBigNumbers = numbers
.filter(v => v > 10)
.map(v => {
console.log('current item:', v);
return v * 2;
})
.reduce((acc, v) => acc + v, 0)
Main difference between array method chaining vs pipeline is:
In array method, for the first step (first array method call) it goes through entire array then switches to the next step,
In Pipeline, for the first item it goes through all the steps then switches to the second item etc...
No that is how iterator methods/lazy evaluation works, pipes kinda do something similar to the '.' chaining, the only difference is the '.' binds the variable to the 'this' of the called function and the '|>' just fills the first argument or the '%' variable.
you're right, i was sloppy reading the tc39
F# syntax FTW! btw I didn't know about the comma operator, really really cool.
when can I use it in my code ?
Never. It's eternal propose
TC39 can't be bothered @@alerya100
Y'know i see things like this and it makes me appreciate powershell's approach a little bit more.
Can't wait this to land. And I like the second option w
It's not a new proposal, I've been waiting for this feature for years now and it doesn't seem we'll get it anytime soon.
Guess I like the f-sharp syntax the most since it allows you to specify "just" the name of the unary function.
Just in time for my coffee break!
Hmm... I don't particularly like it without built in functions for the operations. I mean yeah, console.log in the middle of the chain is nice, but you could do that before with any other method like .map, just return x at the end to not change the array. For now looks like more characters for less functionality.
It would be nice for some things, if you had to apply the same transformation in multiple places and you could easily extract it to a separate function for example, then you wouldn't need to write `|> (x) => fn(x)`, you could just write `|> fn`, which I think is the intended use case. But 90% of my maps/filters/reduces are one-time things.
Maybe curried functions would be beneficial too for this, make a map function that returns an unary function, then you could skip the (x)=>x.map(fn), could just do map(fn). Seems bad for performance though. Not that the standard functions are good since they duplicate the array each time.
Overall, I'm on the fence. I thought I want this, but after seeing live code I'm a bit underwhelmed.
While .map does work for most situations, it would be nice to have a way to access the entire array in the middle of the chain - especially for quick debugging. With map, you just see an element at a time - which can be clunky if you are concerned with the entire thing (for example, you want to count what passes through a filter). I'm not sure how useful it would be, but I routinely find myself wishing this existed. Honestly, just having a .pipe() added to the array object would be great for me.
It's a bit semantically different. If you console.log in the middle of the map it will print each element x, instead of the array of xs. That said, I don't really see the value either
@@bobthemagicmoose a terrible hack for that is:
arr.map((x, index, array)=>{
if ( index===0) console.log(array)
return x
})
You get the entire array as the third parameter in map. I never found a use case for it outside of debugging, and even then it's pretty rare.
Of course keep in mind this will be pretty slow for large arrays, as it still iterates over the whole thing.
Honestly I'd just want the .forEach to return the original array instead of void and everything would be golden, you could just put it in the middle without modifying anything and without allocations. And it's not like that would have any impact on existing things or on performance (I don't think at least)
Great video as always. And of course except the proposals I learned even someth new that I didnt know about js.
Kind of like the F# version better from what you are showing, but saying that what is the size complexity of these?
will probably depend on the V8 implementation of the methods used in these. Right now I think it just copies the entire thing from one pipe to another. Could be probably massively improved, but would require lots of work from the V8 engineers.
I dont think they will have %. It currently is opposite from entire js philosophy
Hack can be anything. Jack just used %. My issues is not the % per sey, but the fact it is using something that should have never been put into the compilation to begin with. var x = (1, 2, 3); // x == 3 // should just throw an error or x equals 1, 2 or 3 (like a random number)
3:20 are you sure putting console.log on the left in same way as in left won't work?
You could wrap a console log in a forEach, or a map, or add a new function to the Array prototype as some suggested. But if you're suggesting you can just put a `.` in front of the console log and have it chain, that would not work.
It would have been better to not use chained functions since chaining like this is already nice. It’s when the function is not on the output but is just a normal function that this is really nice. When c(b(a(result))) becomes result |> a |> b |> c that it’s really clean
I wouldn't use it.
Thanks a lot for preparing this video and showing us this new syntax, Jack. I hate it. :)
I vote hack pipe, the reason why is other languages that use unary operators build functions to be data first inherently because it's a standard. But Javascript isn't those langauges, having the flexibility of the hack pipe mines you're more easily able to plug into different types of functions. In general when you're doing functional programming it's pretty common that you WONT use a method off an array like this and instead your code would look like
// unary would look like
import {filter} from "remeda"
arr
|> filter(n => n.length > 5)
|> n => slice(str, 0, 5)
// hack would look like
arr
|> filter(%, n => n.length)
|> slice(%, 0, 5)
which to me adds a ton more flexibility and matches the js ecosystem more. Otherwise you're going to have to do stuff like make generic curry functions and things of the sort
Can you convert types during a pipeline chain? Array then string?
Yes. The output of each piece of the pipeline can be a different data type. So the expression can start with a string, split it into an array, reformat it into an object, then convert that object to a date, just as a crazy example.
Is the percentage sign (%) still referred to as modulo in this context?
No. And you can change the topic signifier as well. I just use the one from the docs. You could also use something like `^^` which is unused entirely.
TIL about the JS comma operator. 5:48
Of course I knew it could be used in the different parts of the for-loop signature, when multiple variables are in play, but I did not know it had this return behavior. THANK YOU.
you've never looked at minified javascript eh?
function chaining doesn't always work. I've run into a couple of instances where I had to break the chain into separate lines of code (if I recall, one of them was regarding date manipulation).
At the same time, Unless pipelining is tangible faster/more efficient, I'm not sure why there's even a need for it.
I don't think anyone "needs" this.
F# proposal actually makes sense.
I mean I get it: it makes nested function calls cleaner. But the inherit complexity this brings to the table is not something I see as net positive.
TypeScript is already getting way carried away with its features and starting to smell like C++. And many people choose pure JS because of that. Now we’re making JS more convoluted too?
Imagine trying to maintain a simple and readable code base and a junior dev comes all hyped up with this stuff.
This is awesome!
How do errors work?
It is worrying for me to overload the `%` modulo operator as a $in variable. I wonder why # or @ was not chosen? I know of now function for these, yet. Other than that, the hack syntax seems nice.
As someone who uses this feature all the when using functional programming, I'd say this is a feature long time coming.
I really don't know what's the benefit? just adding more ways to do the same things and making it harder for devs to work on existing projects. That's why I'm loving Go atm and getting away slowly from js.
I assume it would work on more data structures than just arrays? Such as promises, result types, etc?
@@majorhumbert676 Yeah I guess so, but I really don't think it's needed or if it will bring any value. But I guess we will have to wait and see.
If you come from functional languages and see the functional core of the javascript language it's consequential to have this missing feature. Pipe operators indicate data flow. It's a hint.
If your background is an object oriented language you will see Javascript as an awkward cousin and introducing pipes make him more awkward.
Comma chain... great to learn something new. I had no idea about that. As far as the syntax goes, I prefer the fSharp syntax. Wondering if there is a 'compose' version of this in the works too. I have been using RamdaJS for 4 years now and love it. Also wondering about async await within the context of the pipelines.
I don't understand, explain me, what is the difference between this and using .map(v => (console.log(v), v)) ?
I'd prefer if they introduce a .tap method for side effects processing similar to rxjs.
Ironically, the hack version is what powershell uses for piping commands, with $_ being the token value
The hack syntax solves piping of the await and yield operations, while the F# syntax are always being rejected by the tc39.
I don't get it. Isn't `x |> fn` the same as fn(x) ? Is the goal just to reduce nesting? I feel like a helper function like `myApply(array, ...funcs)` would do just as good a job. The overhead would be negligible compared to the Array allocations.
Thank you for the video! While watching, I got curious whether this approach would be optimized differently on various engines. Since currently, for small arrays, it's still convenient to use a chain of methods, but for large arrays, it's better to use a single loop with multiple conditions
You could use the comma operator for the F# syntax too
Thx, I am a big fun of pipeline operator. My vote if F# version, because that is really strict to use. 3 years ago I am using proposal pipeline operator in real world react app and that is so usefull in many cases. But this days I turn to no compiled HTML/JS direction, so I am waiting to pipeline operator are passing the proposal state.
The secret is that you dont need neither pipeline op, neither map/filter/foreach. Just use reduce.
I have been trying to get the F# version into PHP for a while. I'm actually prepping a new attempt right now that I hope I can squeeze into the next release. (If voters go for it.)
I have never liked the Hack version, even if it is a PHP fork. I'd much rather work with pure functions than have to bend everything into an expression. I want to pre build the steps in advance.
The operator could be used with any data type and is meant to flatten nested function calls.
The example with arrays is a poor one because arrays is actually a data type in JavaScript that already allows you to chain function calls.
But would the code look like without Array prototype functions? You'd get something like
reduce(map(filter(numbers)))
But with chaining you get
numbers.filter().map().reduce()
However, what if you're not dealing with arrays? For example, what if you want to pass the result of reduce into another function?
fun(numbers.filter().map().reduce())
Suddenly, Array's prototype functions don't help us. What we'd like to do is
numbers.filter().map().reduce() |> fun
This proposal has been around for some time now, and it's only at stage 2. I REALLY hope we get this, but I'm afraid it might take a while
Why do you want this? In this example it's more characters, less readable and the character '|' is pain in the ass to write with some keyboard layouts.
@@jonikyronlahti I and most of the people i know are already using pipe() functions when writing js/ts. That's exactly this but without a special syntax (so more ugly and less general). I guess you'd want to get a different perspective on that, try writing some elixir or F# in your spare time
@ I feel like the pipe syntax is already pretty nice in libraries like rxjs or effect. Adding this to the language itself is a lot of work for little gain imo. Every browser engine has to implement it and then Typescript has to add it, etc. I would much rather see something like patter matching in JS than this. Ofc it's not a "either/or", but there should be some prioritization.
@ Well effect has function composition for pattern matching too. I don't think there is a bottleneck in adding syntax support considering that a babel plugin implementation of these pipes already exists. What really stops us from getting this good stuff is how long looking at proposals takes the committee. Implementation is barely an issue.
Would it work for other data types than arrays? Result types and promises for example
|> amazing!
F# version is my favorite. It's more predictable and the syntax is more readable.
That is super cool.
Thanks Jack!
Are those Monads? If yes, I don't want anything to do with it 🤣🤣🤣
No, these are not monads. Monads are a way to store state in immutable systems.
Elixir has been doing it since its origin and it fits in functional world. JS trying to be something else when it’s not purely functional .. hmm 🤔
Thanks for the video, I think the F# approach is a lot more readable. Personally, I would have preferred another key than the "|" it's just annoying to type!
That's exciting. Looking forward to this implementation... in a few years?
I *think* I like the hack version more - but before I settle on either or, I'd need to understand and compare the differences between F# and Hack, other than what you've just shown here.
Looks like RxJS and observables or stream processing.
In that case,just implenent "tap" for arrays
0:05 you multiply all elements by 2, then filter out the odd elements? they will all be even
Obviously if we get this operator, we will need new versions of filter, map and reduce, so we don't have to wrap this in yet another silly function closure.
If you could bug TC39 to pull their finger out and get this sorted that would be great.
They have been sitting on this for years it's really nice syntactic sugar
so I can use same operator in javascript just like I used in influxDB
The F# way seems more faithful to the JS philosophy. The other one doesn't look like JS at all.
Yeah, the only other place that I can think of that has anything remotely like a "topic" is regex'es with their $1...
A lot of confused people in the comments, I think a look at promise chaining would have been a better example. Promise chaining is basically the F# version already and has the issue of not being flexible with argument order. While the hack version looks more "magical"/"scary", I think it is the more practical option.
One day javascript will just converge to Elm.
I don't get the point of adding more characters to every line. What is the advantage here? The logging?
More control. You aren't limited to just the chaining methods in arrays if you want to do this kind of chaining/pipeling.
I think it can make code easier to understand and write, especially for less experienced programmers (not me btw).
I can't tell you how many times I've seen a sort of inverse pattern in code where you have to work from the 'inside out',
eg function3(function2(function1('value')))
or like was alluded to in the video
tn1 = function1('value');
tn2 = function2(tn1);
/* etc */
(Edit: I am wrong about the following statement )The latter example being especially egregious in larger web applications because generally speaking declaring unnecessary variables should be avoided for performance reasons.
Its nice for little one-off statements that don't need to be refactored into an explicit function.
@@eqprog Those intermediate arrays still exist though, they are just unnamed temporaries that are tagged for garbage collection. The same thing would happen with all of the named temporaries on the way to building the final array that's retained because there would most likely be other references to it.
@@jherr thanks for the correction. I updated my comment to reflect that it’s inaccurate in case someone else reads it and doesn’t read your follow up.
I sort of figured that the pipeline operator would work like this under the hood actually. In your opinion, would the former example be faster or are they essentially the same?
@@eqprog Hmmm, just ranch a benchmark and the chaining, at least in the case that I tested, was marginally faster than the broken out version. There are probably some optimizations the v8 engine can make to reduce the amount of checking required in the chaining implementation.
I like the concept. But I definitely think it should go with using F# instead of Hack.
its ugly,,,but is it possible to return new type in each pipeline? or you stricted to the first type like in your example array?
Looks similar to promise chaining.
Second one seems cleaner. I think it would be easier to rewrite existing chains into the new way. It is clearer that something is a method used on array instead of function that array is passed into.
I didn’t know about that about parenthesis and expressions.
But there would be new versions of filter, map, reduce that allow you to not wrap it in anonymous functions
I’m surprised the F# syntax has been rejected, I find that one easier to understand by just looking at it and feel like any developer can figure it out
The hack syntax introduces % which doesn’t make sense to me as a JS dev but maybe there’s something I’m missing
is that eternal proposer?
Frankly it comes with a lot of boilerplate. Why don't we replace the chain/dot operator instead, that would turn it into a pipeline?
FP all the way.
Yes to support pure functions.
Probably people who don’t like it have never used functional programming languages.
🤓
I like the idea of pipes, but i hate this.
Honestly, this is asking for more problem than solving any
The F# syntax is clearly superior, so obviously they will choose the hack
Rescript lang because the future is now!
Isn't this just syntactic sugar for `[].map()`? You could have just written: `.map((x) => { console.log(x); return x;})`. Less characters and easier to read.
Javascript is a syntaxis sugar for LISP
@ LISP is syntactic sugar for ASSEMBLY
For arrays, yes. But other types don't have .map().
@ So the pipe syntax would work with any object that has `*[Symbol.iterator]`? And that would allow it to work with async iterators etc?
No, that would print each element of the array on its own line. The example code only calls console.log once
nice % sign
I am really interested in your editor than these tutorials if possible can we have video on it 😅
I love F# but without partial application, its syntax seems quite annoying to use.