Syntactic sugar for using a function as if it were a method of its first argument

# Claude Pache (11 years ago)

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

# Jasper St. Pierre (11 years ago)

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.

# Claude Pache (11 years ago)

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

# C. Scott Ananian (11 years ago)

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.

# Andrea Giammarchi (11 years ago)

( 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

# Nathan Wall (11 years ago)

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

# Nathan Wall (11 years ago)

(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:

  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

Nathan

# Jasper St. Pierre (11 years ago)

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.

# Andrea Giammarchi (11 years ago)

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

# Brendan Eich (11 years ago)

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

# Jasper St. Pierre (11 years ago)

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.

# Tab Atkins Jr. (11 years ago)

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.

# Claude Pache (11 years ago)

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 :

# Claude Pache (11 years ago)

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?

# Andrea Giammarchi (11 years ago)

I did indeed !!! Interesting, thanks for the clarification.