viksit 6 years ago

Greenspuns tenth rule: “Any sufficiently complicated C or Fortran program contains an ad-hoc, informally-specified, bug-ridden, slow implementation of half of Common Lisp.”

I’ve always wanted a clojure style thread operator [1] in other languages :)

[1] https://clojure.org/guides/threading_macros

  • emmanueloga_ 6 years ago

    It is interesting to note the overhead it has to define such simple construct in JavaScript. Pages and pages of a spec, all the work to herd ppl behind the proposal, etc.

    In Clojure, the threading macros are awesome and they are not even a language construct, just some ~10 lines of code per macro! [1]

    In ML, the pipe operator is even shorter:

        `let (|>) x f = f x`
    
    1: https://github.com/clojure/clojure/blob/4ef4b1ed7a2e8bb0aaaa...
    • andreareina 6 years ago

      I used to think the same, until it was pointed out to me that the semantics have to be nailed down exactly, so that conforming implementations (e.g. Chrome/FF) do the same thing. This includes figuring out how (if at all) it works with generators and async functions, if you want to support implicit lambdas, etc[1]. Clojure and ML (and related languages I imagine) have the advantages that it is expressible in code which at least makes the specification part simpler.

      Fortunately the simple version is easily approximated, I have this sprinkled in various files where it's useful:

          const pipeline = (val, ...funcs) => funcs.reduce((a, b) => b(a), val);
      
      [1] https://babeljs.io/blog/2018/07/19/whats-happening-with-the-...
  • jhanschoo 6 years ago

    The converse is also true when you want to do side-effecty stuff in Haskell.

maxfurman 6 years ago

What happened to the bind operator (::)? I do miss threading macros from Clojure but I don't see them as solving a real problem in the javascript language. A shortcut for binding `this`, on the other hand, would have saved me a lot of time wondering why my React component methods weren't working correctly.

  • meandmycode 6 years ago

    It went through a bunch of iterations that simplified it, it used to be the abstract references operator which I believe would have a symbol lookup on the target function that would then allow the function to take over execution, thus allowing you to decide if the function would explicitly take the called upon object as `this` or as the first argument.

    It gradually got simplified but nobody could agree about the behaviour of if this would _just work_ with existing functions via the `this` or the first argument, I think eventually the proponents if it grew tired of the battle, they couldn't get unified support for one way or the other.. interesting the pipeline operator has basically gone through the exact same issues, but seems to have had much more attention to it.

    Ultimately I believe the bind operator was just too soon, generally at the time people were still trying to come to terms with what was a lot of new syntax coming to javascript, as well as react and similar things, now I think these chaining operators make a lot more sense to a lot more people.

  • adregan 6 years ago

    You can use the fat arrow syntax to implicitly bind a function to this:

        handleChange = e => this.setState({ value: e.currentTarget.value })
    • rpeden 6 years ago

      Wouldn't that be problematic if you're binding lots of handlers, though? It seems like the fat arrow syntax here is creating a new anonymous function for every instance of the handler.

      And if you've got a list of tens or hundreds of items, and you want a click handler for each, you quickly get to the point where all the extra overhead matters.

      Like, for instance, in a React render function you could have

        {things.map(thing => {
          return <Thing onClick={e => this.handleClick(thingId) } />
        })}
      
      or

        {things.map(thing => {
          return <Thing onClick={this.handleClick.bind(this, thingId)} />
        })}
      
      But option #1 would be a lot less efficient if you've got lots of things, since it's creating a new closure that calls the instance method instead of just pointing at the instance method like snipper #2 does.

      I still see lots of people recommending #1 for this exact scenario, though. Maybe I'm just wrong, or maybe stuff like this is why I see so many SPAs consuming shocking amounts of memory.

      I grant that using the fat arrow is probably fine for the scenario you mentioned though, with something like a text field. I don't think I've seen many pages with so many text fields that the extra overhead would even make a difference.

      • alxlu 6 years ago

        Having .bind inside a JSX prop also creates a new anonymous function on each render.

        To avoid it, you can do (sorry for bad formatting):

          class Thing extends React.Component {
            handleClick = (thingId) => {
              this.setState(/*stuff*/);
            }
            render() {
              return <div>{things.map(thing => <Thing onClick= 
           {this.handleClick} />)}</div>
            }
          }
        
        You can also bind handleClick inside the constructor without the arrow.

          constructor(...args) {
            super(...args);
            this.handleClick = this._handleClick.bind(this);
          }
          _handleClick(thing) {
            // stuff
          }
        • mercer 6 years ago

          The latter version seems to be the one I most commonly see, except _handleClick() is just handleClick() repeated. Am I correct in assuming this is considered idiomatic at this point?

          • antonkm 6 years ago

            Yes, I'd say so.

            • mercer 6 years ago

              Is this better somehow? I feel like I should know this, but it's Friday night and I'm tired, but isn't the constructor fired on every instance of the component? And if so, is this a better approach for any particular reason other than clarity?

              • aenigmaclamo 6 years ago

                It is fired on every instance of the component. However, if you create the arrow function in the render function, it'll be created for every render. Since React keeps the component instances around between renders, it can be a lot better.

      • _greim_ 6 years ago

        I don't have any benchmarks so I won't speculate which is faster, but both these options create a new function object. The fact that one is done implicitly doesn't necessarily make it faster. I suspect performance will be highly engine- and version-specific, so best to choose the more idiomatic one (which I leave as an exercise for the reader to figure out).

    • purplerabbit 6 years ago

      The moment es6 classes showed up without automatic method binding was the moment I fell out of love with JavaScript.

      I can't imagine that there would EVER be a good reason not to have a method bound to the class. Now we've all been forced to learn this new property initializer syntax and add yet another babel dependency to facilitate it.

  • nemothekid 6 years ago

    There was a proposal for decorators that clearly handled this that seems to have been deprecated (it’s not included in Babel anymore).

        @autobind
         handleClick: () -> { return this.member }
    
    The whole binding in the constructor seems like needless noise to me
alvis 6 years ago

Note that the proposal for a pipeline operator is still in its early day. In fact, there are two proposals (https://babeljs.io/blog/2018/07/19/whats-happening-with-the-...) and so far there's no clear winner. Seems like there's still a long way to go.

Anyway, which style do you prefer?

  • deckar01 6 years ago

    Neither. Just use a function. You don't need a dedicated operator in the language's syntax to composite functions.

        const foo = pipeline(
            bar,
            baz,
            x => x + 1,
        )
        foo(42)
    • jahewson 6 years ago

      Now try writing the type declaration for your pipeline function...

      • deckar01 6 years ago

            pipeline : (...fns : Function[]) => Function
      • roblabla 6 years ago

        a -> (a -> b) -> (b -> c) -> c

dvlsg 6 years ago

Don't they still have to wait for the spec to finalize / hit stage 3?

Don't get me wrong, I'm super excited for this functionality because I love it in languages like F# and Elixir, but it looks like the pipe proposal is still stage 1.

https://github.com/tc39/proposals/blob/master/README.md

  • k__ 6 years ago

    While I think this is a risky move, I had the impression that the ratification of new features was mainly based on the implementations.

    So, first the runtimes/compilers implement it and then it gets into the spec.

    TypeScript sees itself as a superset of ECMAScript, so its ECMAScript parts are just another implementation of the spec, like Node, Babel and the browser engines.

  • magicalist 6 years ago

    > Don't they still have to wait for the spec to finalize / hit stage 3?

    Yes, this appears to be just a tracking issue, not an impending implementation (and definitely not a PR). The "Waiting for TC39" label added even has the tooltip "Unactionable until TC39 reaches some conclusion".

  • minxomat 6 years ago

    Livescript (which was supposed to replace CoffeeScript) also has it.

skybrian 6 years ago

It seems unfortunate that JavaScript is evolving so quickly these days. There is more syntax than anyone who isn't a full-time web developer will be able to remember.

  • lucideer 6 years ago

    There's been a lot of new features added in ES6+ but not a huge amount of new syntax overall (a lot of the new features are things like array and object methods, etc.)

    All of the new syntax is technically optional, but at least some percentage of it just makes it easier for newer/less-experienced devs to write things: e.g. classes as simplified syntactic sugar around prototype-based inheritance models, or function parameter spread operators to make learning how to arguments.whatever unnecessary and generally make code for dynamic argument lengths easier to understand.

    No matter when you started learning Javascript there would have been parts too complex or obscure to learn immediately (the prototype-based inheritance model is something I think a lot of full-time professional JS devs don't fully grok). There was always things you learn first and things you learn later. With the new syntax, I think now the things* you learn first are just more likely to be immediately useful.

    * where "things" excludes learning how to master every trending new library—that is a part of Javascript that isn't improving unfortunately.

    • meandmycode 6 years ago

      I find this to be a bit of a cop out, there's still less syntax than something like C# or Java, and plenty of developers are ok with those.. JavaScript went through years of nothing and then sudden change.. if you were only casually interested in it (say, a backend developer who just puts some js together) then criticizing seems unfair.. you can continue to write the exact same code, but expect those who write it as their primary language care a hell of a lot more about it being succinct and powerful.

AndrewDucker 6 years ago

I was hoping we'd get something like PowerShell pipelines, which work on iterators, passing each item through as it's generated.

Those are incredibly powerful, and as a lot to the language, whereas this just seems like fairly simple syntactic sugar.