Autobinding destructuring

# David Bruant (13 years ago)

I wrote some code today:

 var cos = Math.cos,
     sin = Math.sin,
     PI = Math.PI;

 // later:

 x1 = x + R*cos(t)*cos(angle) - r*sin(t)*sin(angle);

First of all, it made me realize that the usual example of 'with' (using with(Math) and an expression like I showed) turn out to be not so useful if we had destructuring (turning my 3 top lines into only one)

But it also made me realize that by default, destructuring returns unbound methods. It's perfect for the above use case, but may be annoying when you wish to extract functions bound to the object they're extracted from:

 var o = {a:1, f: function(){return this.a;}};
 var {f} = o;
 f(); // throw

It reminded me of an ECMAScript Regret submitted by Tom [1] about the fact that method are extracted unbound by default. And after a couple of tweets related to 'with' and the canvas API [2][3][4] I wonder: would it be worth having another syntactic form doing bound method extraction? I don't have an idea of what the syntax would look like, but it seems like a valuable idea.

David

[1] DavidBruant/ECMAScript-regrets#13 [2] twitter.com/getify/status/231500606425145344 [3] twitter.com/rwaldron/status/231501699183960065 [4] twitter.com/rwaldron/status/231503439526518784

# David Bruant (13 years ago)

Le 03/08/2012 17:45, David Bruant a écrit :

(...) It reminded me of an ECMAScript Regret submitted by Tom about the fact that method are extracted unbound by default. And after a couple of tweets related to 'with' and the canvas API I wonder: would it be worth having another syntactic form doing bound method extraction? I don't have an idea of what the syntax would look like, but it seems like a valuable idea.

Here is how it would look like (adapted from [1]):

 var context = 

document.getElementsByTagName('canvas')[0].getContext('2d');

 // bikeshed syntax for binding destructuring, my point isn't about 

syntax here var #{beginPath: begin, moveTo, lineTo, stroke, closePath: end} = context; // extracted methods are bound to the context object.

 begin();
 for(var t=0; t <= 2*PI; t += DRAWING_STEP){
     x0 = x1;
     y0 = y1;
     x1 = x + R*cos(t)*cos(angle) - r*sin(t)*sin(angle);
     y1 = y - R*cos(t)*sin(angle) - r*sin(t)*cos(angle);
     moveTo(x0, y0);
     lineTo(x1, y1);
     stroke();
 }
 end();


 // Draw Major and Minor Axes
 context.strokeStyle = "#FF0000";
 begin();
 moveTo(x+R*cos(angle), y-R*sin(angle));
 lineTo(x-R*cos(angle), y+R*sin(angle));
 moveTo(x+r*sin(angle), y+r*cos(angle));
 lineTo(x-r*sin(angle), y-r*cos(angle));
 stroke();
 end();

To me, this looks easier to read than the original. I have no special bound to the #{} syntax; I just used that to differenciate from non-binding destructuring. A feature that would be nice would be to be able to nest binding and regular destructuring, but I don't know if my bikeshed syntax allows that.

David

[1] DavidBruant/ShapeGuesser/blob/1a69b7a0a86b6f22225e3b89b31038028d99a478/shapeguesser.js#L21

# Brendan Eich (13 years ago)

David Bruant wrote:

var context = 

document.getElementsByTagName('canvas')[0].getContext('2d');

// bikeshed syntax for binding destructuring, my point isn't about 

syntax here var #{beginPath: begin, moveTo, lineTo, stroke, closePath: end} = context; // extracted methods are bound to the context object.

The # was somewhat wanted for records, tuples, and const functions, once.

Appreciate that the # syntax is straw, so permit me to light the match. The challenge is to put the |this| binding on the left-hand side, without making the pattern language not be covered by the object and array literal subgrammar or a plausible extension of it. One idea:

 var {beginPath: begin, moveTo, lineTo, stroke, closePath: 

end}.bind(context) = context;

In a binding or even lvalue context, this would make a special form without adding syntax or reserving bind otherwise. It does require repeating |context| on LHS where that occurs on the RHS, though.

For DRY, new syntax seems required. From

strawman:bind_operator

we would write

 var begin = ::context.beginPath;

to extract just one bound method reference.

(Note that :: was wanted for guards too, so its use here is straw of a flammably dry kind. Note also that the grammar in the strawman under "Syntax" is not complete.)

The equivalence between let foo = obj.bar; and let {bar: foo} = obj; suggests

 var {beginPath: begin, moveTo, lineTo, stroke, closePath: end} = 

::context;

but this separates the :: on the RHS from the destructuring on the LHS.

Could we instead make :: part of the pattern language too? It would be covered by the (corrected) grammar in Dave's bind operator proposal.

 var ::{beginPath: begin, moveTo, lineTo, stroke, closePath: end} = 

context;

Your wish to mix auto-bound and unbound method destructuring could be met by:

 var {unbound, ::bound} = object;

which could be written without destructuring, but with the bind operator strawman, as

 var unbound = object.unbound, bound = ::object.bound;
# Claus Reinke (13 years ago)

But it also made me realize that by default, destructuring returns unbound methods. It's perfect for the above use case, but may be annoying when you wish to extract functions bound to the object they're extracted from:

var o = {a:1, f: function(){return this.a;}};
var {f} = o;
f(); // throw

Agreed that this is an obstacle. With operators, one could have a convenient binding selection, but the destructuring case needs a separate solution.

// bikeshed syntax for binding destructuring, var #{beginPath: begin, moveTo, lineTo, stroke, 

closePath: end} = context; // extracted methods are bound to the context object.

This version, I have issues with: not all items need to be bound, so we'd need syntax for subpatterns. In this example, all of the bound items are going to be bound to the same context, and that context is on the right hand side, so perhaps we need to do something there?

let bind_methods = obj =>
        Proxy(obj,{get(t,n,r) {
            let  prop = t[n];
            return prop instanceof Function ? prop.bind(t) : prop }});

var {beginPath: begin, moveTo, lineTo, stroke, closePath: end} =
    bind_methods(context);
# Claus Reinke (13 years ago)

[argh, unhelpful hidden key-combo led to premature send]

But it also made me realize that by default, destructuring returns unbound methods. It's perfect for the above use case, but may be annoying when you wish to extract functions bound to the object they're extracted from:

var o = {a:1, f: function(){return this.a;}};
var {f} = o;
f(); // throw

Agreed that this is an obstacle. With operators, one could have a convenient binding selection, but the destructuring case needs a separate solution.

// bikeshed syntax for binding destructuring, var #{beginPath: begin, moveTo, lineTo, stroke, closePath: end} = context; // extracted methods are bound to the context object.

This version, I have issues with: not all items need to be bound, so we'd need syntax for subpatterns. In this example, all of the bound items are going to be bound to the same context, and that context is on the right hand side, so perhaps we need to do something there? Perhaps

let bind_methods = obj => Proxy(obj,{get(t,n,r) { let prop = t[n]; return prop instanceof Function ? prop.bind(t) : prop } });

var {beginPath: begin, moveTo, lineTo, stroke, closePath: end} = bind_methods(context);

Of course, that won't work if we need binding destructuring from sub-objects.

Claus

# David Bruant (13 years ago)

Le 04/08/2012 16:19, Brendan Eich a écrit :

David Bruant wrote:

var context = 

document.getElementsByTagName('canvas')[0].getContext('2d');

// bikeshed syntax for binding destructuring, my point isn't 

about syntax here var #{beginPath: begin, moveTo, lineTo, stroke, closePath: end} = context; // extracted methods are bound to the context object.

The # was somewhat wanted for records, tuples, and const functions, once.

Ok. As I try to highlight each time I talk about syntax, I'm usually more interested in having a new syntax for something than in the exact syntax. Syntax and ES grammar really are not my specialty. When I propose something I make sure it's a syntax error in current engines, but that's as far as I go. So really, the exact syntax proposal can be thrown away if there are better ideas or if it conflicts with something else.

For DRY, new syntax seems required. From

strawman:bind_operator

The idea of combining the bind operator and bound destructuring is a good one in my opinion.

Your wish to mix auto-bound and unbound method destructuring could be met by:

var {unbound, ::bound} = object;

which could be written without destructuring, but with the bind operator strawman, as

var unbound = object.unbound, bound = ::object.bound;

+1, if that syntax works, that's fine for me :-)

# David Bruant (13 years ago)

Le 04/08/2012 17:53, Claus Reinke a écrit :

[argh, unhelpful hidden key-combo led to premature send]

But it also made me realize that by default, destructuring returns unbound methods. It's perfect for the above use case, but may be annoying when you wish to extract functions bound to the object they're extracted from:

var o = {a:1, f: function(){return this.a;}};
var {f} = o;
f(); // throw

Agreed that this is an obstacle. With operators, one could have a convenient binding selection, but the destructuring case needs a separate solution.

Why does it need a separate solution? Especially if it's possible in destructring to have both bound and unbound?

// bikeshed syntax for binding destructuring, var #{beginPath: begin, moveTo, lineTo, stroke, closePath: end} = context; // extracted methods are bound to the context object.

This version, I have issues with: not all items need to be bound, so we'd need syntax for subpatterns. In this example, all of the bound items are going to be bound to the same context, and that context is on the right hand side, so perhaps we need to do something there? Perhaps

let bind_methods = obj => Proxy(obj,{get(t,n,r) { let prop = t[n]; return prop instanceof Function ? prop.bind(t) : prop } });

var {beginPath: begin, moveTo, lineTo, stroke, closePath: end} = bind_methods(context);

Of course, that won't work if we need binding destructuring from sub-objects.

Indeed. It also does not combine bound and unbound. To some extent, the fact that you've written "prop instanceof Function" to test whether prop is a function enforces the idea that it should be done through syntax and not a library ;-)

# Claus Reinke (13 years ago)

But it also made me realize that by default, destructuring returns unbound methods.
... Agreed that this is an obstacle. With operators, one could have a convenient binding selection, but the destructuring case needs a separate solution. Why does it need a separate solution? Especially if it's possible in destructring to have both bound and unbound?

Yes, that was misleading. Let me try again:

1 where possible, destructuring should have expression equivalent

2 autobinding selection/destructuring can be done for a whole context object, or for selected methods in it

3 there have been suggestions for autobinding selection, ie, an expression solution, on a per-method basis

4 your strawman suggested a destructuring solution, on a whole-context-object basis

Brendan's suggestion gives us a destructuring equivalent to an existing expression strawman, on a per-method basis.

I was trying to give an expression equivalent to your destructuring suggestion, on a whole-context-object basis.

That gives two separate pairs of suggested solutions.

Claus