tc39 / proposal-bind-operator

This-Binding Syntax for ECMAScript
1.74k stars 30 forks source link

Explicit naming of `this` #30

Open gilbert opened 8 years ago

gilbert commented 8 years ago

Hi all, I wanted to gather your opinions on the explicit-this proposal since I think it is directly related to this proposal. Personally, it would put me fully on board with moving the pipeline functionality of es-function-bind forward (not that I'm anyone important).

To save you a click, here is a code example of what the explicit-this proposal would allow:

function array.zip (otherArray) {
  return array.map( (a, i) => [a, otherArray[i]] )
}

function array.flatten () {
  return array.reduce( (a,b) => a.concat(b) )
}

function subject.delayedLog () {
  setTimeout( () => console.log(subject), 1000 )
}

[10,20]
  ::zip( [1,2] )
  ::flatten()
  ::delayedLog()
benjamingr commented 8 years ago

For analogy, C# does something like:

function flatten (this array) {
  return array.reduce( (a,b) => a.concat(b) )
}

Disadvantages are mostly the fact that any existing code cannot be used, for example, you won't be able to call Array.prototype.map on a NodeList.

gilbert commented 8 years ago

for example, you won't be able to call Array.prototype.map on a NodeList.

Could you explain this point further? I don't quite understand what you mean.

Artazor commented 8 years ago

@mindeavor - imagine that you already have an implementation of the virtual method you need in some other class (i.e. Array).

var {map} = Array.prototype;
var anchors = document.querySelectorAll("a");
var urls = anchors::map( a => a.href );

Here querySelectorAll returns a NodeList - something that is Array-like (with .length and indexed access) but is not inherited from an Array.

How would you rewrite this example with explicit this naming?

gilbert commented 8 years ago

The explicit this naming proposal only affects how you write the definition. Otherwise it runs the same way as before.

function array.zip (otherArray) {
  return array.map( (a, i) => [a, otherArray[i]] );
}

// Equivalent to:

function zip (otherArray) {
  var array = this;
  return array.map( (a, i) => [a, otherArray[i]] );
}

To be clear, I am not proposing any notion of types, as function flatten (this array) { seems to imply.

zenparsing commented 8 years ago

@mindeavor Thanks! As has been discussed many times, the primary objection to the :: operator is that it uses this as a parameter, and as such you don't have the ability to do naming, destructuring, etc. in the parameter list.

The syntax you present is a cool solution to that problem, although I think that having Identifier . Identifier is going to be a little confusing given the fact that it appears to be a member expression, but actually introduces to separate bindings. I think a more likely syntax would be the C# style:

function zip(this array, otherArray) {
  return array.map( (a, i) => [a, otherArray[i]] );
}

That would also allow you to do destructuring:

function something(this { foo, bar }) {
    // ...
}

What do you think?

In any case, the :: operator needs to stand on its own, with "explicit this" parameters as a potential follow-up.

spion commented 8 years ago

I like this idea.

That said, @zenparsing is that really the main objection, the reason why bind isn't moving forward? I don't see anyone objecting to class methods being unable to destructure this. Or is it default argument values? I don't see how that is a problem either...

Really though, why isn't this proposal moving? I think many high-profile projects are excited about it. Babel implemented it, in TypeScript it got general approval, RxJS 5 implements bind operator versions of their operators here: https://github.com/ReactiveX/RxJS#es6-via-npm and calls it the best overall method, which Angular 2 developers agree with ... Whats exactly is missing to get it moving to other stages, and (since I'm getting off-topic now) can we open a checklist issue with it? I'm really excited about it, and would be really disappointed to see it wither.

domenic commented 8 years ago

There are a couple TC39 members who are against it, which is enough to block progress despite any amount of community enthusiasm :(

gilbert commented 8 years ago

@zenparsing I like it too. It exposes this for what it is: just another parameter. I updated the proposal readme with the new syntax.

@spion The this-bind operator is a functional concept – it allows you to decouple functions from data. I think no one objects to non-destructurable class methods because destructuring isn't that useful in OOP.

zenparsing commented 8 years ago

@spion It's great to see the positive reactions. Regarding TC39, timing is also an issue, since the committee is quite busy at the moment trying to get other things moved through (e.g. async functions). And honestly, we've needed the time to work through all of the input and alternatives brought up in this repo. I'll try to get at least an informal feel from the committee next week though.

Alxandr commented 8 years ago

I'll try to get at least an informal feel from the committee next week though.

Somewhat off topic, but where would "updates" (so to speak) be posted? Like, how do I know if this is moving forward, backwards, or not at all?

robotlolita commented 8 years ago

Given that:

x::foo(bar) === foo.call(x, bar)

A syntax introducing the thing that matches how it's used seems more reasonable to me.

So, given something like:

function this :: foo(arg1, arg2) {
  ...
}

something :: foo(arg1, arg2);

You have the same symmetry as regular function calls:

function foo(arg1, arg2) {
  ...
}

foo(arg1, arg2);

And since the concept of this is both reified in the language, and matches the way people'd use it, explaining it would be a lot simpler — even though backwards compatibility would allow people to define functions without giving an explicit this binding.

johnsonjo4531 commented 8 years ago

@Alxandr I would check out where the proposals are progress wise on this page https://github.com/tc39/ecma262#ecmascript. The defintion of the stages for the proposals are given here https://tc39.github.io/process-document/. This proposal is in stage 0 which the stage 0 specs are on their own page here https://github.com/tc39/ecma262/blob/master/stage0.md.

xooxdoo commented 8 years ago

i agree with @robotlolita

Swivelgames commented 7 years ago

Can the same not be achieved using

function foobar() {
    const { foo, bar } = this;
} 

I'm quite excited at the prospect of the :: bind operator syntax. It seems much more promising than parameterizing this. If destructuring this is not currently possible given the current destructuring syntax, I think that should be looked at and proposed separately.

Naming this does not seem any more useful than simply using this, especially given that explicitly naming this and destructuring should be easily achieved using other means currently available with minimal effort. For what it's worth, I feel like a bind operator solves a more common use case.

robotlolita commented 7 years ago

@Swivelgames destructuring works with any expression on the RHS.

The idea of naming this is more to make it easier to explain the language to JS programmers than anything. Arrows cover most of the use cases where you'd want to bind this to some name to keep around without being shadowed in inner scopes, and you usually would only run into problems if you used JS as a prototype-based language, and constructed objects inline.

If you have:

const Foo = {
  value: x,
  foo() {
    const outerThis = this;
    return {
      bar() {
        return this.qux(outerThis.value);
      },
      qux(value) {
        return value + 1;
      }
    }
  }
}

Named this parameters would make it much simpler:

const Foo = {
  value: x,
  outer::foo() {
    return {
      bar() {
        return this.qux(outer.value);
      },
      qux(value) {
        return value + 1;
      }
    }
  }
}
Swivelgames commented 7 years ago

@robotlolita I understand that use case. Good point. However, my opinion remains the same that I believe this bind operator would be more beneficial. I still believe the benefits of the bind operator far outweigh the explicit name of this, especially given that previously written methods will be out of reach, among some of the other disadvantages. And the prospect of virtual method libraries, which I have several use-cases for. But, to each their own! :)

robotlolita commented 7 years ago

@Swivelgames they're not mutually exclusive proposals, but rather meant to complement each other.

Jamesernator commented 7 years ago

Just another syntax idea for explicit naming:

function foo(bar) for baz {
    ...
}

someBaz::foo(someBar)

With a more concrete example

function map(iteratee) for observable {
   return new Observable((observer) => {
       observable.forEach(item => {
           observer.next(iteratee(item))
       }).then(_ => observer.complete())
}