tc39/proposal-bind-operator

Introduction of new operator

Haringat opened this issue · 13 comments

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.

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

@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.

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.

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);

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.

@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: "));

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.

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

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.

"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.

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