Named `this` and `this` destructuring

# Jussi Kalliokoski (9 years ago)

It's probably a bit early for this, but I figured I'd put it out there (I already proposed this as a tangent in the function bind syntax thread). This syntax proposal is purely about convenience and subjective expressiveness (like any feature addition to a Turing complete language).

As I've been building Trine, I noticed that the "this as data" pattern is extremely powerful and expressive, however the code in the functions doesn't convey the intent very clearly. For example:

function add (b) { return this + b; }
function * map (fn) { for ( let item of this ) { yield item::fn(); } }

vs.

function add (a, b) { return a + b; }
function * map (iterator, fn) { for ( let item of iterator ) { yield
item::fn(); } }

Also currently neither Flow or TypeScript support type annotating this. There's discussion [1] [2] in both the projects for allowing this to be specified as a parameter to allow annotating it, e.g.

function add (this : number, b : number) : number { return this + b; }

This leads to my current proposal, i.e. being able to make the first parameter of the function an alias for this by using a special prefix (&). This would not only allow aliasing this, but also destructuring and default values (as well as type annotation in language extensions).

The different forms and their desugarings:

function add (&a, b) {
  return a + b;
}

// would desugar to

function add (b) {
  var a = this;
  return a + b;
}


function multiplyTuple (&[a, b], multiplier) {
  return [a * multiplier, b * multiplier];
}

// would desugar to
function multiplyTuple (multiplier) {
  var [a, b] = this;
  return [a * multiplier, b * multiplier];
}


function multiplyPoints (&{ x1: x, y1: y }, { x2: x, y2: y }) {
  return { x: x1 * x2, y: y1 * y2 };
}

// would desugar to
function multiplyPoints (_p2) {
  var { x1: x, y1: y } = this;
  var { x2: x, y2: y } = _p2;
  return { x: x1 * x2, y: y1 * y2 };
}


// allow passing the element for mocking in tests
function isQsaSupported (&dummyElement = document) {
  return typeof dummyElement.querySelectorAll !== "undefined";
}

This proposal would also be consistent with the type annotation proposals for this mentioned earlier.

WDYT?

[1] facebook/flow#452 [2] Microsoft/TypeScript#1985

# C. Scott Ananian (9 years ago)

Could you include some examples of calling functions defined this way? The most obvious way uses Function#call and would be terribly awkward. Perhaps I'm just overlooking some ES6 feature which makes passing a specific this value easy?

It seems curious that you are not using methods instead of functions.

# Andrea Giammarchi (9 years ago)

Mostly every Array extra in ES5 would work with those functions, e.g.

function multiplyPoints (_p2) {
  var { x1: x, y1: y } = this;
  var { x2: x, y2: y } = _p2;
  return { x: x1 * x2, y: y1 * y2 };
}

var multiplied = manyPoints.map(multiplyPoints, centralPoint);

It's not that common pattern but it gives you the ability to recycle functions as both methods or filters or mappers or forEachers and vice-versa.

I personally use those kind of functions quite a lot to be honest, most developers keep ignoring Array extra second parameter as context though, they probably use a wrapped fat arrow within an IFI with call(context) :D

Best

# Allen Wirfs-Brock (9 years ago)

On Jun 17, 2015, at 8:09 AM, Andrea Giammarchi wrote:

Mostly every Array extra in ES5 would work with those functions, e.g.

function multiplyPoints (_p2) {
  var { x1: x, y1: y } = this;
  var { x2: x, y2: y } = _p2;
  return { x: x1 * x2, y: y1 * y2 };
}

var multiplied = manyPoints.map(multiplyPoints, centralPoint);

It's not that common pattern but it gives you the ability to recycle functions as both methods or filters or mappers or forEachers and vice-versa.

I personally use those kind of functions quite a lot to be honest, most developers keep ignoring Array extra second parameter as context though, they probably use a wrapped fat arrow within an IFI with call(context) :D

It seems to me that we already can quite nicely express in ES6 the use of a function as a method:

function multiplyPoints({x1, y1}, {x2,y2}) {
    return { x: x1 * x2, y: y1 * y2 }
}

class Point {
   multiply(p2) {return multiplyPoints(this, p2)}
}

or, perhaps a bit more OO

class Point {
   static multiply({x1, y1}, {x2,y2}) {
      return new Point(x1 * x2, y1 * y2 )  //or new this(...) if you care about subclassing Point
   }

   multiply(p2) {return Point.multiply(this, p2)}

   constructor(x,y) {
      this.x = x;
      this.x = y;
   }
}

Regardless of how you express it, if you want the same function to be used both as a standalone function and as an method, you are going to have to have a line or two of code to install the function as a method. To me, the one-line method definitions used above are about as concise and much clearer in intent than Point.prototype.multiply=multiplyPoints; or whatever other expression you would use to install such a function as a method. And I would expect any high perf JIT to use inlining to completely eliminate the indirection so, where it matters, there probably wound't be any performance difference.

Many JS programmers have historically been confused about the JS semantics of this because it is over-exposed in non-method functions. Things like the current proposal increases rather than mitigates the potential for such confusion. if you are programming in a functional style, don't write functions that use this. If you need to transition from to/from OO and functional styles, be explicit as shown above.

this is an OO concept. FP people, this is not for you; don't use it, don't try to "fix it".

# Andrea Giammarchi (9 years ago)

I forgot to mention that those functions I use as both methods and map/filters are most of the time private , and yet we haven't introduced private methods within current class specification.

Functions with a this are very portable/handy within closures, combined with Array extras and .call/.apply to use them as private methods has been very well known and used pattern since ES3

I'm not saying this proposal improve much in current ES6 specification, I'm saying there are very valid use cases for having a this in a non method function.

# Jussi Kalliokoski (9 years ago)

On Wed, Jun 17, 2015 at 7:13 PM, Allen Wirfs-Brock <allen at wirfs-brock.com>

wrote:

On Jun 17, 2015, at 8:09 AM, Andrea Giammarchi wrote:

Mostly every Array extra in ES5 would work with those functions, e.g.

function multiplyPoints (_p2) {
  var { x1: x, y1: y } = this;
  var { x2: x, y2: y } = _p2;
  return { x: x1 * x2, y: y1 * y2 };
}

var multiplied = manyPoints.map(multiplyPoints, centralPoint);

It's not that common pattern but it gives you the ability to recycle functions as both methods or filters or mappers or forEachers and vice-versa.

I personally use those kind of functions quite a lot to be honest, most developers keep ignoring Array extra second parameter as context though, they probably use a wrapped fat arrow within an IFI with call(context) :D

It seems to me that we already can quite nicely express in ES6 the use of a function as a method:

function multiplyPoints({x1, y1}, {x2,y2}) {
    return { x: x1 * x2, y: y1 * y2 }
}

class Point {
   multiply(p2) {return multiplyPoints(this, p2)}
}

or, perhaps a bit more OO

class Point {
   static multiply({x1, y1}, {x2,y2}) {
      return new Point(x1 * x2, y1 * y2 )  //or new this(...) if you care
about subclassing Point
   }

   multiply(p2) {return Point.multiply(this, p2)}

   constructor(x,y) {
      this.x = x;
      this.x = y;
   }
}

Regardless of how you express it, if you want the same function to be used both as a standalone function and as an method, you are going to have to have a line or two of code to install the function as a method. To me, the one-line method definitions used above are about as concise and much clearer in intent than Point.prototype.multiply=multiplyPoints; or whatever other expression you would use to install such a function as a method. And I would expect any high perf JIT to use inlining to completely eliminate the indirection so, where it matters, there probably wound't be any performance difference.

Many JS programmers have historically been confused about the JS semantics of this because it is over-exposed in non-method functions. Things like the current proposal increases rather than mitigates the potential for such confusion. if you are programming in a functional style, don't write functions that use this. If you need to transition from to/from OO and functional styles, be explicit as shown above.

this is an OO concept. FP people, this is not for you; don't use it, don't try to "fix it".

But I already am 1, and it allows for a much nicer syntax than functions that don't use this, and also composes well with built-ins (other than Object.*) This proposal is building on the proposed function bind syntax 2.

More examples of the power of the bind syntax can be found in the links, but the bind syntax combined with my proposal would for example allow this:

function add (&a, b) { return a + b; }

2::add(3) // 5
# Andrea Giammarchi (9 years ago)

OK that one I've no idea what supposes to improve exactly ... I should have tried to realize your proposal better, apologies.

After seeing that, I probably agree with Allen at this point we don't really need that kind of syntax around JS (still IMHO, of course)

Best

# Jussi Kalliokoski (9 years ago)

On Wed, Jun 17, 2015 at 10:35 PM, Andrea Giammarchi < andrea.giammarchi at gmail.com> wrote:

OK that one I've no idea what supposes to improve exactly ... I should have tried to realize your proposal better, apologies.

After seeing that, I probably agree with Allen at this point we don't really need that kind of syntax around JS (still IMHO, of course)

To each their own. :) I personally really like the bind syntax and have received a tremendously positive feedback on it - the Trine project alone has received over 1000 stars on GitHub, in under a week since release (last Thursday), and it's just showcasing a part of the power of the proposed syntax.

# Andrea Giammarchi (9 years ago)

the ::bind syntax is OK (I don't really like that double colon 'cause it's not semantic at all with the single colon meaning but I can live with it) but having potential bitwise-like operators around to adress a possible context ... well, I wouldn't probably use/need that in the short, or even long, term.

Again, just my opinion, listen to others ;-)

# Allen Wirfs-Brock (9 years ago)

On Jun 17, 2015, at 10:01 AM, Jussi Kalliokoski wrote: ...

More examples of the power of the bind syntax can be found in the links, but the bind syntax combined with my proposal would for example allow this:

function add (&a, b) { return a + b; }

2::add(3) // 5

and why that better than:

function add(a,b) {return a+b}

add(2,3);

Every new feature increases the conceptual complexity of a language and to justify that it needs to provide a big pay back. This doesn't seem to have much of a pay back. Adding the & and :: doesn't eliminate the need for JS programmer to learn about this in functions for the various already existing ways to call a function with an explicit this value. It just add more new syntax that needs to be learned and remembered are move feature interactions that have to be understood.

JS doesn't need more syntax and semantics piled on to this. Ideally some would be taken away. However, the latter is not possible.

# Jason Kuhrt (9 years ago)

Exactly what Allen said.

Adding syntax to work around a bad feature is an awful idea. We should be trying to reduce and remove usage of this by reducing resistance to other ways of programming in JavaScript.

Minimal API Surface areas apply to languages, not just libraries.

2014.jsconf.eu/speakers/sebastian-markbage-minimal-api-surface-area-learning-patterns-instead-of-frameworks.html, 2014.jsconf.eu/speakers/sebastian-markbage-minimal-api-surface-area-learning-patterns-instead-of-frameworks.html

# Jussi Kalliokoski (9 years ago)

On Wed, Jun 17, 2015 at 10:45 PM, Andrea Giammarchi < andrea.giammarchi at gmail.com> wrote:

the ::bind syntax is OK (I don't really like that double colon 'cause it's not semantic at all with the single colon meaning but I can live with it) but having potential bitwise-like operators around to adress a possible context ... well, I wouldn't probably use/need that in the short, or even long, term.

Again, just my opinion, listen to others ;-)

Ah sorry, my bad, I misunderstood you. :) To clarify, I've only heard positive feedback from people of the bind syntax; as for this proposal, this thread is the first time I hear feedback and it doesn't seem overtly positive. :P

# Jussi Kalliokoski (9 years ago)

On Wed, Jun 17, 2015 at 11:38 PM, Allen Wirfs-Brock <allen at wirfs-brock.com>

wrote:

On Jun 17, 2015, at 10:01 AM, Jussi Kalliokoski wrote: ...

More examples of the power of the bind syntax can be found in the links, but the bind syntax combined with my proposal would for example allow this:

function add (&a, b) { return a + b; }

2::add(3) // 5

and why that better than:

function add(a,b) {return a+b}

add(2,3);

Poor example, sorry.

Because of chaining in a way that preservers the natural read order of JS and fitting in well with the builtins:


flatten(
    items // <- data here
        .filter(isOk)
        .map(toOtherType)
).join(" ");

vs.


items // <- data here
    .filter(isOk)
    .map(toOtherType)
    ::flatten()
    .join(" ");

As for fitting in (composing) well with the builtins, for example Trine allows you to do this:

[["a", "b", "c"], ["e", "f", "g"]]::map([].slice); // yields copies of the
arrays
["foo", "bar"]::map("".repeat::partial(3)) // yields "foofoofoo",
"barbarbar"

because all the functions take data in the this slot, as most of the builtins and framework methods do too.

Every new feature increases the conceptual complexity of a language and to justify that it needs to provide a big pay back.

I wholeheartedly agree on this, which is why I stated that it might be too early for my proposal.

This doesn't seem to have much of a pay back.

Have no comments to that for my proposal. As for Kevin's proposal, I disagree; the payback seems great in that one.

Adding the & and :: doesn't eliminate the need for JS programmer to learn about this in functions for the various already existing ways to call a function with an explicit this value. It just add more new syntax that needs to be learned and remembered are move feature interactions that have to be understood.

Agreed, however "not having to learn this" isn't the goal of either of the proposals mentioned in this thread.

JS doesn't need more syntax and semantics piled on to this.

Arguably JS hasn't "needed" anything since it became turing complete. That doesn't mean that adding some of the things added since was a bad idea. It's always a tradeoff, but that's nothing specific to this proposal.

Ideally some would be taken away. However, the latter is not possible.

Agree on this too. (As a tangent I'd be curious to know what you'd take away, were it possible?)