tc39 / proposal-bind-operator

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

Introduction of new operator #58

Open Haringat opened 2 years ago

Haringat commented 2 years ago

To better differentiate between "bind and call" and "only bind, but do not call" I would introduce a new ::: operator beside the :: one. The :: would only bind a function to the value before it:

const x = value::func(123);

would be equivalent to

const x = func.bind(value, 123);

The ::: operator on the other hand would call the bound function in-place:

const x = value:::func(123);

would be equivalent to

const x = func.call(value, 123);

The following would also work:

const x = value::(higherOrderFunc(123));

and would be equivalent to

const x = higherOrderFunc(123).bind(value);

And with ::::

const x = value:::(higherOrderFunc(123))();

which would be equivalent to

const x = higherOrderFunc(123).call(value);

So the two use-cases (which both occur frequently) would both be handled properly.

ljharb commented 2 years ago

Why would we need to differentiate? Its a call if there’s invoking parens, it’s not if there’s not.

Haringat commented 2 years ago

@ljharb Because that is not true in the examples stated above because the :: operator could also be used for property binding as described in the first example.

ljharb commented 2 years ago

ah, i see what you mean.

This seems like it would be penalizing all the common cases in service of avoiding the need for parens in one exceedingly rare use case.

Haringat commented 2 years ago

I am not sure but maybe it could be arranged that for :: the parens are optional so that

const x  = value::func;

and

const x = value::func();

would be identical and both desugar to

const x = func.bind(value);
ljharb commented 2 years ago

That would be far, far worse. The former should only bind, and the latter should only call, and the mistake made with new where invoking parens are optional should never be repeated.

Using higher-order functions at all is rare; using receiver-sensitive higher-order functions should be almost nonexistent. Just use parens for these cases.

Haringat commented 2 years ago

@ljharb and how would you create parameter bindings with that approach? Say, you want to fix the first argument to a set value and only want the function to be called with the other arguments. Here is an example that comes to mind:

function logValue(prefix, loggable) {
    this.log(prefix + loggable);
}

values.forEach(console::logValue("prefix: "));
ljharb commented 2 years ago

ok so, a few things. First of all, I simply wouldn't do that, because it's a standalone function and shouldn't use this at all. However, if I had to for some reason, I'd do values.forEach(logValue.bind(console, 'prefix: ')). iow, this syntax is explicitly NOT intended to allow you to bind arguments, only the receiver.

Haringat commented 2 years ago

Where does it state that the syntax was explicitly not intended for that?

ljharb commented 2 years ago

The entire readme discusses the receiver and doesn't mention arguments, for seven years, as does the strawman linked in the readme, via archive.org which dates back many years before that.

Haringat commented 2 years ago

"it does not state anything about that" is not the same as "it is explicitly not intended for that". Explicit would mean that it actually states somewhere that this is not supposed to work.

ljharb commented 2 years ago

Fair enough, it doesn’t state it. But through every discussion of it I’ve been present for, it was explicitly the purpose.

Pro2stinger commented 8 months ago

This isn't needed. Instead of value:::func(arguments) this could be used value::func()(arguments).

Haringat commented 8 months ago

@Pro2stinger this would require func to always return a bound function. This would be a hassle to write, at which point using Function.prototype.call would actually be more convenient.