Closed aaronshaf closed 8 years ago
In the second example (map(_val, x => x.character())
), what would this
be set to?
I think there are three main benefits to binding over partials:
foo::bar
and bound-reference with ::foo.bar
)this
, which seems like a significant portion of event handlers and the likethis
) and Java's method reference (equivalent of bind
), both of which use ::
If we think of ::
as an unary operator that places binding, ::(...)
would only be binding the arguments as it does not have a holder to refer to as the this
value. It would not bind the holder and thus not a this
value. This even gives a slight advantage of doing something that Function.prototype.bind
does not, partially completing methods of classes.
@bmeck As currently specified, ::(expr)
actually does bind the this
value. expr
must be a "Reference" (think member lookup expression), and the parens are unwrapped before applying the operation to the reference.
This is actually how other Reference-taking prefix operators, like delete
work:
let obj = { x: 100 };
delete ((((obj.x))));
obj.x; // undefined
And ::
:
let obj = { x: 100, method() { return this.x } };
let m = ::((((obj.method))));
m(); // 100
@zenparsing
your operand to ::
and delete
is obj.prop
.
my thought was when the operand is a [[FormalParameters]]:
let obj = { f() {} };
delete obj.f(); // doesn't make sense when we are dealing w/ fn invocation / cannot specify the operand is the [[FormalParameters]]
obj.f; // f() {}
or
global.x = 'global';
let obj = {method(prefix) {return prefix + this.x}, x: 'property'};
m = obj.method::('is a');
m(); // global
For the currently defined object::function
syntax, I somewhat agree. So instead of:
function map(callback) {
return this.map(callback);
}
You mean:
function map(array, callback) {
return array.map(callback);
}
From a documentation perspective that does make a lot of sense. It also allows for the functions to be used two different ways:
var a = [...];
map(a, function() {...});
a::map(function() {...});
@zenparsing did my comment make sense to you?
For what it's worth, the "first parameter" notation is what C# does with extension methods, and the this
proposal is what Java does with default interface implementations.With swift, the this
approach (self) is taken.
Overall I think this shows that both approaches are viable. I think dynamic this
in JS is more like the current proposal, the current proposal makes sense, and both ways can be called anyway. Just like people do [].map.call(...
today, they'll be able to use ::
.
@benjamingr @aaronshaf If I just look at this issue in isolation, then it seems to me that the "first param" approach is slightly better: slightly easier to grok, since it doesn't rely on dynamic this
, and slightly easier to use, since you can use the resulting function both with and without the ::
.
However, it then wants a different operator. The best that I can think of would be thin-arrow:
iterable
-> map(...)
-> takeWhile(...)
-> forEach(...);
And that would leave ::
for method extraction, similar to Java:
Promise.resolve(123).then(console::log); // instead of ::console.log
Not bad, but I'm not sure we can justify taking thin-arrow for a pipe operator. What do others think?
Honestly I think we should just move forward with ::
.
Notation in both cases is pretty good, this proposal is really a substential improvement of JS for a lot of people. Multiple people have used it (and abstract refs before it) in babel and really like the concept.
I think taking ->
as well has huge bikeshed potential and ::console.log
is something people can get used to very fast.
FWIW, I like @zenparsing's proposal. :-\
@benjamingr Unfortunately, some TC39 participants are strongly opposed to using this
in such a loose way.
The only weird thing about using ::
as infix method extraction is computed property names:
console::log; // This is fine
console::['log']; // Is this right? Looks a little odd... Do we even allow computed names here?
@benjamingr Unfortunately, some TC39 participants are strongly opposed to using this in such a loose way.
What do you mean by loose?
The only weird thing about using :: as infix method extraction is computed property names:
Computed property names sound good, but it can be added at a later point.
@benjamingr Loose, meaning using this
as an arbitrary "zeroth" parameter. Some feel that "this" should only be used in a more traditional OO way, and loose this
leads to confusing code. I'm sympathetic to that viewpoint.
e.g. https://esdiscuss.org/topic/named-this-and-this-destructuring
It's definitely not "arbitrary", this is about extending objects from the outside a-la C# extension methods or Swift extensions. I do not hold the opinion that ::
violates oop, on the contrary - it brings JavaScript the ability to extend objects in a scoped way and puts it on par with other OOP languages which already share that ability :)
It uses this
for context.
I completely agree with @benjamingr on that matter. Move forward with ::…
as method extraction and …::…
as virtual method calls. It's what we need, it's what people are interested in primarily, it's what this proposal was about from the begin. (Although I'm not opposed to make method extraction an infix operator [not ::
then?], as it wouldn't change the spirit of the operator).
A syntax for first-parameter-passing (->
) is too much bikeshedding, and honestly we don't need it. Chaining works well in an OOP style with method invocations (data as zeroth argument) and in a functional style with data as the last argument (forEach(…, takeWhile(…, map(…, iterable)))
with your example) which goes well with currying and composition (if you're into that stuff). Data as the first argument is just awkward (think nested underscore calls), and we don't need extra syntax to encourage the creation of such functions (that don't use this
anyway).
Sticking with the current proposal.
I originally posted this to the wrong repo:
https://github.com/gsklee/bound-native-methods/issues/3
Compare:
With:
I may not have phrased my proposal correctly, but consider that with lodash I could:
Bound virtual methods assumes we want to be using context, this, etc. My proposal is perhaps more "functional"?
Perhaps there is more convenience in chaining utility functions than reusing prototype methods?
@zenparsing responded already here https://github.com/gsklee/bound-native-methods/issues/3#issuecomment-109432573, but I'd like to move the conversation here. He wrote: