Syntactic sugar for using a function as if it were a method of its first argument
It's fairly incomprehensible to me, and doesn't really have any advantages over writing it out the long way:
Object.getOwnPropertyDescriptor(window.HTMLFormElement.prototype,
'elements').get
window.HTMLFormElement.prototype{Object.getOwnPropertyDescriptor}('elements').get
They're both the same line length. I find the former more direct and the latter more confusing. Namely, the whole ('elements') looks like a method call containing one argument, rather than having a secret hidden argument as its first.
Le 27 mai 2014 à 17:04, Jasper St. Pierre <jstpierre at mecheye.net> a écrit :
(...) Namely, the whole ('elements') looks like a method call containing one argument, rather than having a secret hidden argument as its first.
Yes, it was exactly intended to appear as such, making obj.{Object.getOwnPropertyDescriptor}('elements')
similar to obj.hasOwnProperty('elements')
, for instance. If it helps, you could think of foo.{Bar.baz}
as defining a temporary method: (function(...args) { return Bar.baz(this, ...args) })
, or: Bar.baz.bind(Bar, foo)
.
I like the idea, but I agree that the .{ } syntax isn't quite right. For one thing, on my screen the () are visually very similar to {}, while [] are easily distinguished. The leading dot is also a bit odd. I'd be interested in seeing some more alternative syntaxes for this idea.
( already covered to receive stones )
Object.defineProperty(
Object.prototype,
'through',
{
enumerable: false,
configurable: true,
writable: true,
value: function through(callback) {
for (var a = [this], i = 1; i < arguments.length; a[i] =
arguments[i++]);
return callback.apply(null, a);
}
}
);
so that ..
window.HTMLFormElement.prototype.through(Object.getOwnPropertyDescriptor,
'elements').get
document.querySelectorAll('input[type=checkbox][name=
Select_ID]:checked').through(Array.from).map(e => e.value).join(',')
Last, but not least, I don't see any concrete advantage/need neither ^_^
Best
I have a syntax proposal, but it goes along with a slightly different way of thinking of this.
The proposed bind operator[1] can take a function which acts as a method and make a call to it with a specific receiver without the receiver needing to have the method defined as a property (basically a nicer syntax for call
and apply
). Given a bind operator, this problem reduces to just having a way to take a function and turn its first argument into its this
. So good syntax can be achieved in two steps.
Steps:1. Turn the function into a method.2. Invoke with bind operator.
To solve step 1, we could have something like Function.curryThis
which does the opposite of "uncurryThis":
Function.curryThis = function(f) { return function(...args) { return f(this, ...args); }; };
Example use:
// ES5 Array.from(obj);
// Proposal var toArray = Function.curryThis(Array.from); obj::toArray();
Another example:
// ES5 Object.getOwnPropertyDescriptor(window.HTMLFormElement.prototype, 'elements').get;
// Proposal var getOwnPropertyDescriptor = Function.curryThis(Object.getOwnPropertyDescriptor); window.HTMLFormElement.prototype::getOwnPropertyDescriptor('elements');
Perhaps sugar could be added to do both steps in one with another operator (say :::
).
// ES5 Object.getOwnPropertyDescriptor(window.HTMLFormElement.prototype, 'elements').get;
// Proposal window.HTMLFormElement.prototype:::Object.getOwnPropertyDescriptor('elements');
It's the same proposal as yours with slightly different syntax. And I think the syntax makes some sense given a bind operator ::
.
[1] strawman:bind_operator
Date: Tue, 27 May 2014 11:04:52 -0400 Subject: Re: Syntactic sugar for using a function as if it were a method of its first argument From: jstpierre at mecheye.net To: claude.pache at gmail.com CC: es-discuss at mozilla.org
It's fairly incomprehensible to me, and doesn't really have any advantages over writing it out the long way:
Object.getOwnPropertyDescriptor(window.HTMLFormElement.prototype, 'elements').get
window.HTMLFormElement.prototype{Object.getOwnPropertyDescriptor}('elements').get
They're both the same line length. I find the former more direct and the latter more confusing. Namely, the whole ('elements') looks like a method call containing one argument, rather than having a secret hidden argument as its first.
On Tue, May 27, 2014 at 4:17 AM, Claude Pache <claude.pache at gmail.com> wrote:
Often a function can be thought as if it were a method of its first argument. Compare:
Array.from(obj); /* vs */ obj.toString()
Object.getPrototypeOf(obj); /* vs */ obj.__proto__
Array.forEach(obj, func); /* vs */ obj.forEach(func)
Math.clz32(num); /* vs */ num.toFixed(2)
and note the inversion of the order of the terms.
So, I propose to introduce syntactic sugar to replace the terms in correct order, e.g.,
foo{Bar.baz}(...args)
// or (to be bikeshed)
foo.{Bar.baz}(...args)
as a synonym of:
Bar.baz(foo, ...args)
Here are two examples of use:
window.HTMLFormElement.prototype{Object.getOwnPropertyDescriptor}('elements').get
document.querySelectorAll('input[type=checkbox][name=Select_ID]:checked').{Array.from}().map(e => e.value).join(',')
The wins are:
-
a strict left-to-right order, instead of having methods appearing alternatively at the right and at the left of its main operand, thus improving readability;
-
enabling the use of the Existential Operator for the new form, e.g.:
window.HTMLFormElement?.prototype?{Object.getOwnPropertyDescriptor}('elements')?.get
(Note that ?{
suffers from the same grammar problem as ?[
and ?(
, and the due fix for the two latter would also fix the former.)
(Sorry about the formatting in the last one. Trying again.)
I have a syntax proposal, but it goes along with a slightly different way of thinking of this.
The proposed bind operator[1] can take a function which acts as a method and make a call to it with a specific receiver without the receiver needing to have the method defined as a property (basically a nicer syntax for call
and apply
). Given a bind operator, this problem reduces to just having a way to take a function and turn its first argument into its this
. So good syntax can be achieved in two steps.
Steps:
- Turn the function into a method.
- Invoke with bind operator.
To solve step 1, we could have something like Function.curryThis
which does the opposite of "uncurryThis":
Function.curryThis = function(f) { return function(...args) { return f(this, ...args); }; };
Example use:
// ES5 Array.from(obj);
// Proposal var toArray = Function.curryThis(Array.from); obj::toArray();
Another example:
// ES5 Object.getOwnPropertyDescriptor(window.HTMLFormElement.prototype, 'elements').get;
// Proposal var getOwnPropertyDescriptor = Function.curryThis(Object.getOwnPropertyDescriptor); window.HTMLFormElement.prototype::getOwnPropertyDescriptor('elements');
Perhaps sugar could be added to do both steps in one with another operator (say :::
).
// ES5 Object.getOwnPropertyDescriptor(window.HTMLFormElement.prototype, 'elements').get;
// Proposal window.HTMLFormElement.prototype:::Object.getOwnPropertyDescriptor('elements');
It's the same proposal as yours with slightly different syntax. And I think the syntax makes some sense given a bind operator ::
.
Nathan
I'm not sure I like it. Given how other languages use the "::" operator, I'd expect "Foo::bar" to do some sort of static property lookup for a name called "bar" on "Foo", not bind the local variable "Foo" to the local variable "bar". I think "bar.bind(Foo)" is more than enough. I am OK with your "curryThis" function, though, even if I think it's a bit too fancy for any code I'd write.
Sorry Nathan but how is this different from extending Object prototype? you
are basically polluting everywhere ::
operator, can't see any less
conflictual scenario than just "polluting the .
one" in terms of prototype
Jasper St. Pierre wrote:
I'm not sure I like it. Given how other languages use the "::" operator, I'd expect "Foo::bar" to do some sort of static property lookup for a name called "bar" on "Foo", not bind the local variable "Foo" to the local variable "bar".
That's not what the proposed bind operator does.
I think "bar.bind(Foo)" is more than enough.
That allocates a new (and subtly different) bound function object. Part of the motivation for the bind operator is to avoid the allocation (and the subtle difference, secondarily).
From my reading of the email and strawman page, let f = obj::foo; is
exactly equivalent to let f = foo.bind(obj);
Am I wrong? How is the result subtly different?
Really, with "obj::foo", I would expect "obj::foo" to be the same as "obj.foo.bind(obj);", not "foo.bind(obj);" And even then, I don't think it's worth it for a new syntax, since we already have the automatic method closures for the new class syntax in ES6.
On Tue, May 27, 2014 at 12:40 PM, Jasper St. Pierre <jstpierre at mecheye.net> wrote:
From my reading of the email and strawman page, let f = obj::foo; is exactly equivalent to let f = foo.bind(obj);
Am I wrong? How is the result subtly different?
Brendan's saying that the return value of foo.bind(obj) is subtly different from foo itself.
Really, with "obj::foo", I would expect "obj::foo" to be the same as "obj.foo.bind(obj);", not "foo.bind(obj);" And even then, I don't think it's worth it for a new syntax, since we already have the automatic method closures for the new class syntax in ES6.
It is technically the same, but when you write obj::foo()
, the
function is immediately called and thrown out in favor of the
function's return argument. This means it's also identical to
foo.call(obj), which is cheaper as there's no allocation involved.
This sounds good to me. Just a nit, you should define:
Function.curryThis = function(f, base = undefined) {
return function(...args) {
return f.call(base, this, ...args);
};
};
so that you can do, e.g.,
class ImprovedArray extends Array { /* _not_ overloading Array.from */ }
var toImprovedArray = Function.curryThis(ImprovedArray.from, ImprovedArray)
without needing an extra .bind
.
—Claude
Le 27 mai 2014 à 20:20, Nathan Wall <nathan.wall at live.com> a écrit :
Le 27 mai 2014 à 20:59, Andrea Giammarchi <andrea.giammarchi at gmail.com> a écrit :
Sorry Nathan but how is this different from extending Object prototype? you are basically polluting everywhere
::
operator, can't see any less conflictual scenario than just "polluting the.
one" in terms of prototype
There is no need to pollute anything. I guess you missed that the RHS of the ::
operator could be lexically scoped?
I did indeed !!! Interesting, thanks for the clarification.
Often a function can be thought as if it were a method of its first argument. Compare:
and note the inversion of the order of the terms.
So, I propose to introduce syntactic sugar to replace the terms in correct order, e.g.,
as a synonym of:
Here are two examples of use:
The wins are:
a strict left-to-right order, instead of having methods appearing alternatively at the right and at the left of its main operand, thus improving readability;
enabling the use of the Existential Operator for the new form, e.g.:
window.HTMLFormElement?.prototype?{Object.getOwnPropertyDescriptor}('elements')?.get
(Note that
?{
suffers from the same grammar problem as?[
and?(
, and the due fix for the two latter would also fix the former.)