11 ms·
JS things I didn’t know existed
- turblety 9y agoWhat a lovely article. Was interesting and learned about the comma operator, which is really awesome.
- santoriv 9y agoInteresting read. I quite like the pipe operator as I think it would make a lot of code easier to read. I would caution people against using some of these "features" though as writing clever code that leverages obscure techniques is NOT good for readability and maintainability. The person who has to maintain your code will probably be able to more easily grok some boring if else statements than some weird ternary thing that leverages the comma operator. Just because you can doesn't mean you should.
- egeozcan 9y agoA small self-promotion, given the relevant context: I was frustrated with the lack of support for the pipeline operator and came up with this: https://github.com/egeozcan/ppipe https://github.com/egeozcan/ppipe - I currently use it on a couple of projects and it really helped make my code more understandable.
- djedr 9y agoI wonder if there is really a need for this operator. Compare 2 |> square |> increment |> square and pipe(2, square, increment, square) where `pipe` is defined as const pipe = (...args) => args.reduce((prev, curr) => curr(prev)); So given that what would be a strong argument for introducing the operator to JavaScript?
- seanwilson 9y agoIs there a good way to see the intermediate results at each stage in the pipe when things go wrong? Pipes are concise but perhaps harder to debug depending on what tools you have.
- marksomnian 9y agofunction tee(arg) { console.log(arg); return arg; } 2 |> square |> tee |> increment |> square;
- throwanem 9y agoOr even, using another cute toy from the article: const tee = arg => console.log(arg), arg;
- seanwilson 9y agoSo I'm comparing pipe usage to this e.g. const result1 = square(2); const result2 = increment(result1); const result3 = square(result2); With the above, I can add in a breakpoint and inspect what's going on at each stage with access to other debugging tools. Log statements don't seem nearly as good as this. The above is nice as well in that you can give intuitive names to the intermediate results.
- djedr 9y agoIf you need names for intermediate results, etc. then you don't want the pipeline operator/function. In such cases the things you say are true. But when you wan't to be more concise, with the operator/function, you can. BTW A nice feature of a code editor would be if you could magically switch back and forth between code with intermediate named variables and pipelined version of the same code, e.g. by selecting the code and invoking an option.
- deleted 9y ago[deleted]
- djedr 9y agoYou can define a debug version: const pipe_debug = (...args) => args.reduce((prev, curr) => { console.log('pipe debug; curr:', curr, ', prev:', prev) return curr(prev) }) and when you want to see intermediate results of a `pipe` call, you append `_debug` to the name.
- philipwhiuk 9y agoReadability and not having to define a function / include a dependency of common functions (see leftpad).
- djedr 9y agoReadability I would argue is a matter of taste here, so not a very strong argument. As for the second thing, I'd agree, but instead of introducing an operator, we could make the helper a built-in feature.
- ams6110 9y agoYeah readability is largely about what you're used to. I find the nested function calls more readable, as I've used that notation for decades going all the way back to high school math.
- fenomas 9y agoOn readability, all the code samples here are using pipe operators spread out on one line, but for anything nontrivial I think the point is to use it thusly: initialValue |> doSomething |> doSomethingElse |> reportResults In which case I think it's extremely readable compared to the alternatives.
- deleted 9y ago[deleted]
- djedr 9y agoCompare: pipe(initialValue , doSomething , doSomethingElse , reportResults ) Is it really so much more readable with the operator?
- mabbo 9y agoNot that I necessarily disagree, but one counter argument would be "why even use the pipe method? You could just say square(increment(square(2))) and get the same result, right?" The point is readability. It's UX for the programmer, reducing cognitive load in understanding what's going on. Pipes are fantastic in the terminal and have never been replaced there because they are very simple glue piecing together the complex bits with a simple-to-understand model. If we can have that in JS, I'd be quite pleased even if there are other ways to do the same thing.
- djedr 9y agoThe counter argument would be wrong. The advantage of the operator and the `pipe` helper over regular function composition is that you can emphasize the flow of data through a pipeline of functions. The only advantage in readability I see here is that the operator is hard to mistake for anything else, it sticks out of the code, more than a seemingly regular function call. But this is not an advantage that justifies introducing the operator. Other than that, UX and cognitive load is the same. Pipes can indeed be fantastic! ;) * Apropos composition: why not add a composition operator while we're at it? Or we could define something like: const compose = (...args) => pipe(...args.reverse()) and then compose(square, increment, square, 2) ;) For me there is also something more ineffable that's important here. A question of whether adding a new built-in operator fits within the general philosophy or some sort of "look & feel" of the language, or whatever. I think JavaScript seems to be quite conservative, when it comes to bringing in new operators. Perhaps that's a good thing. Maybe not. But so far, I remain unconvinced.
- hajile 9y agoOne great reason is that the composing function can be optimized away much easier with the native operator.
- djedr 9y agoAnd a native operator can be optimized just about the same amount as a built-in function. So maybe add the function instead of the operator to the language?
- hajile 9y agoOne issue with a `pipe` function is that it is an arity of N. That makes it almost impossible to optimize for all the edge cases. In contrast, the pipe operator is always fixed at an arity of 2. Implementing a static Function.pipe in terms of the primitive operator becomes easy enough. Functional languages have played with both the function and the operator, but they keep coming back to the operator because it's more readable. //implementation of your pipe in terms of the (potential) pipe operator Function.pipe = (fn, ...args) => { //let's use a pseudo Duff's device switch (args.length) { case 0: return fn; case 1: return fn |> args[0]; case 2: return fn |> args[0] |> args[1]; case 3: return fn |> args[0] |> args[1] |> args[2]; case 4: return fn |> args[0] |> args[1] |> args[2] |> args[3]; case 5: return fn |> args[0] |> args[1] |> args[2] |> args[3] |> args[4]; case 6: return fn |> args[0] |> args[1] |> args[2] |> args[3] |> args[4] |> args[5]; case 7: return fn |> args[0] |> args[1] |> args[2] |> args[3] |> args[4] |> args[5] |> args[6]; case 8: return fn |> args[0] |> args[1] |> args[2] |> args[3] |> args[4] |> args[5] |> args[6] |> args[7]; default: var mod = args.length >> 3; //div by 8 var rem = -8 * mod + args.length; //mult add fn = Function.pipe(fn, ...args.slice(0, rem)); while (rem < args.length) { fn = fn |> args[rem++] |> args[rem++] |> args[rem++] |> args[rem++] |> args[rem++] |> args[rem++] |> args[rem++] |> args[rem++]; } return fn; } }; It's a little off topic, but I'd love to see the addition of well-known symbols for all the operators to allow custom overloading. Lua does operator overloading with metatables. No reason JS can't too. At that point, pipe, compose, and even spaceship become much more useful. var x = { foo: [1,2,3], [Symbol.add](rightHandSide) {//Binary return this.foo.concat(rightHandSide); }, [Symbol.incr]() {//Unary this.foo = this.foo.map(x => x += 1); return this.foo; } } //default to standard operator if //if left side is primitive or if //left side has no matching Symbol x + 3; //same as x[Symbol.add](3) [0].concat(x++); //=> [0,1,2,3] //same as [0].concat(x); x[Symbol.incr](); [0, 1].concat(++x); //=> [0, 1, 2, 3, 4] //same as x[Symbol.incr](); [0, 1].concat(x);
- marcosdumay 9y agoThis, and void, and promises: > 2 |> square |> increment |> square So, looks like any sufficiently complex language nowadays carries a badly self-compatible, inextensible, limited set of popular Haskell libraries. And also Lisp's seq. Other Lisp features are left for implementation on a Lisp interpreter once your application gets large.
- xixixao 9y agoYou won't be able to type `pipe` with Flow/Typescript. But they can (and had) added support for the |> operator.
- djedr 9y ago> You won't be able to type `pipe` with Flow/Typescript. Don't know what you mean by that. > But they can (and had) added support for the |> operator. Doesn't seem like they did: https://github.com/Microsoft/TypeScript/issues/17718 https://github.com/Microsoft/TypeScript/issues/17718 https://github.com/facebook/flow/issues/5443 https://github.com/facebook/flow/issues/5443
- Pxtl 9y agoImho, languages that break the abribtrary difference between functions and methods do this best, so there's no difference between Square(myNum) and myNum.Square(), since .Square() works nice for pipe-style usage.
- djedr 9y agoSure, but JavaScript is not one of those languages unfortunately.
- dsego 9y agoWe should only use language features which spark joy.
- always_good 9y agoconst x = (1, 2, 3) // 3 x === (1, 2, 3) Javascript's tuples have some great compression!
- yorwba 9y agoWhen I was just a neophyte, I wondered whether JavaScript supported multidimensional arrays, so I wrote array[x, y] = something and it appeared to work (reading array[x, y] returned the written value, after all). It was only when I tried to store multiple values with the same y-coordinate that I realized my mistake.
- pg_bot 9y agoGlad to see that Elixir's pipe operator is beginning to spread to other languages!
- nerdponx 9y agoWas it in Elixir or F# first?
- scns 9y agoBash?
- pg_bot 9y agoTechnically yes, but it is interesting that "|>" seems to be the syntax that is being adopted amongst multiple languages.
- chickenfries 9y agoWell, we can't use "|".
- Pxtl 9y agoAlthough to be fair, using | for "or" has always been silly. Also making the bitwise operator the short one since the logical one gets more usage in the real world. Hindsight is 20/20 of course.
- yorwba 9y agoSurely you mean OCaml's pipe operator? They have had it since at least 2013: http://ocaml.org/releases/4.01.0.html http://ocaml.org/releases/4.01.0.html I wouldn't be surprised if the syntax was even older. And the functionality has likely been used in some academic language from decades ago. It's just reverse function composition after all.
- 9y ago
- kurtisc 9y agoIMO it seems unwise to use features like these which you wouldn't expect other people to know on a shared codebase. C also has the comma operator and, while I find it can be used to write things elegantly, I've yet to find a place where it's easier for another person to understand at first scan than using more common idioms.
- olavgg 9y agoI wholeheartedly agree with you. This is something which I've been struggling with the last 6 months telling a colleague. He always want to use things that are completely new and sometimes just weird. He always believe this new way of coding is better. When the rest of us have to make changes in his code, we have to dig through the MDN documentation to figure out how that code works. We are trying to solve business problems here, we are not interested in unnecessary coding challenges.
- throwanem 9y agoEslint can be configured to forbid language constructs based on the level of standardization they've reached - in both directions; it can as well forbid "var" as it can "let" and "const". You can also include it in your test and CI pipelines, so that code which violates the specified restrictions cannot be shipped at all. We do this where I work, and while I admit I initially found it somewhat confining, the benefit of being able to more quickly grasp a new codebase among the many that constitute our infrastructure has proven extremely valuable over time. Granted, it's a fair bit of work to set up in the first place - more in deciding what to allow and what not to; eslint itself is very friendly in my experience. And someone who just flat-out refuses to play along might not be a problem this tool can solve - although perhaps you can all reach a modus vivendi around an acceptable level of novelty somewhere short of the bleeding edge. (We use TC39 level 3. You might prefer something more conservative, such as level 4, or some version of ECMA-262 proper.) But it's worth a look, I think, as a tool that might reduce the headaches you seem frequently to be stuck with. I hope you find it useful!
- michaelmcmillan 9y agoAgree! With that being said, teams should also strive to learn as much of the language as possible. Languages won't evolve if we don't care to try out new syntax.
- the_gipsy 9y agoThe pipe operator exists exactly like the proposal in Elm, and I miss it so much in js. It doesn’t really make sense to put it in the article, it is completely unrelated to the other arcane and unsafe features that precede it.
- tw1010 9y agoOh, sweet summer child.
- ChrisSD 9y agoI'm surprised the author hadn't heard of void. There was a time when using it in links (aka `javascript:void(0)`) was very common. Also yeah javascript has labels but using them is usually a bad idea. While they may seem useful they can be harder to reason about in all but the simplest code. You're almost always better off restructuring your code instead of using labels.
- tgb 9y agoHow did the void(0) thing work in links? I remember being frustrated by it when I couldn't see where the link would take me, but never understood what was going on.
- k__ 9y agothese links have an onclick handler. If the handler goes missing or fails, this construct will prevent the link from doing what it usually would. But using a button is better in such cases...
- et-al 9y agohttps://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/void https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe... Back then, you'd use it with the `href` attribute to prevent the link from going anywhere (it would return `undefined`), then call a function with the `onclick` attribute. E.g. <a href="javascript:void(0)" onclick="clickHandler()">Click me</a>
- ChrisSD 9y agoBasically it defers to a javascript function instead of using the web's normal way of linking. An example of such a link would be: <a href="javascript:void(0)" onclick="dostuff()">Click me</a> href="javascript:void(0)" basically tells the browser to do nothing when a link is clicked. It's like returning false from an event listener. onclick="dostuff()" will then listen for clicks and call the `dostuff` function to update the page or load a new one. Nowadays it's considered bad practice and to be honest it wasn't exactly good practice in those days but it was very commonly used.
- 9y ago
- deleted 9y ago[deleted]
- ballenf 9y agoSo many functional aspects of JS make for an exciting future. If only chrome/firefox would catch up with webkit with regard to tail calls. The latest comments from the devs on the bug in chrome is just depressing.
- poof131 9y agoSide anecdote, but a number of people at my company were making fun of a candidate who used labels to break out of loops in JS. “Those don’t exist” they joked. I remember thinking, “I thought they did,” but didn’t say anything. The sad part is I really like the people I work with and they are generally smart engineers. There is just such an arrogance to the interview process in tech it is almost unreal. I haven’t noticed a lack of qualified candidates in tech, just a lack of humility in many of the gatekeepers. Whiteboard pissing contests.
- AFNobody 9y ago> Side anecdote, but a number of people at my company were making fun of a candidate who used labels to break out of loops in JS. “Those don’t exist” they joked. I remember thinking, “I thought they did,” but didn’t say anything. The sad part is I really like the people I work with and they are generally smart engineers. There is just such an arrogance to the interview process in tech it is almost unreal. I haven’t noticed a lack of qualified candidates in tech, just a lack of humility in many of the gatekeepers. Whiteboard pissing contests. And this is why if people mention a whiteboard as part of the interview process, I don't bother. I've seen similar arrogance related issues in regards to security/crypto questions. The ability to demonstrate the code actually runs and/or is actually broken from a security perspective is an invaluable part of the process.
- ac2u 9y agoPrecisely the reason I don't like to focus interviews on language gotchas and semantics. The only exception I made was a quick pre-screen (to be completed in their own time with no pressure) in which candidates received 3 or 4 code snippets, and were asked a couple of questions about them which they wrote answers out in plain English explaining their thought process, in however much detail they wanted. (Thinks like a piece of code where "this" would break because of context, or a cross domain request that would fail and some ways to approach solving). Even then, that was used as a ways to say "ok, this candidate clearly has good understanding, we'll not hang about too long during the interview process on that topic". If they didn't mention what we thought the answer was, it didn't count against them at all, it just gave us an area to explore in the interview. (Because who's to say our pre-screen question wasn't the problem?)
- _pmf_ 9y agoUsing the comma operator for branch specific actions in a ternary operator expression is actually really nice.
- bugmen0t 9y agoBet you didn't hear about Symbols either, then
- GordonS 9y agoGiven how long I've had the misfortune of working with JS, I can't believe I wasn't aware of the `dataset` one!
- tritium 9y agoI knew about half of these, and the other half was still pretty awesome. I’m not entirely enthused by the pipeline operator, but oh well.