YAWSI: an identity unary operator

# Allen Wirfs-Brock (14 years ago)

(Yet Another Wacky Syntax Idea)

Here is a relatively common coding pattern:

var a; var obj = { get a() {return a}, set a(v) {a=v} };

Often the intent is that the variable a should only be used within the object literal. The block scoping with let and do operator will allow the variable and object literal to be group in this manner:

let obj = do { let a; ({get a() {return a}, set a()v) {a=v} }) }

Unfortunately, the object literal has to be enclosed in parentheses because an expression statement cannot begin with an object literal. The same would be the case if a function expression was involved rather than an object literal.

Parens are annoying because you have to remember to close them and match them if any additional nesting is involved.

With completion reform, do expressions, and broader use of blocks that produce values we should expect to see more situations where an ExpressionStatement needs to start with an object literal or function expression. It would be nice to have some way to do this that does not require parentheses.

Prefix the expression with an unary operator is sufficient to disambiguate such expression but unfortunately all of the existing unary operators perform conversions that would invalidate the value. However, a new unary operator whose semantics was to simply return its operand value would do the job nicely. Both . and = seem like good candidates for such an operator but any operator symbol that is not currently used in a unary form would be a possibility:

let obj = do { let a; ={get a() {return a}, set a(v) {a=v} } }

let obj = do { let a; .{get a() {return a}, set a(v) {a=v} } }

let obj = do { let a; ^{get a() {return a}, //probably my favorite, but that may just be a leakage from my Smalltalk background... set a(v) {a=v} } }

let obj = do { let a; |{get a() {return a}, set a(v) {a=v} } }

let obj = do { let a; /{get a() {return a}, set a(v) {a=v} } }

let obj = do { let a; *{get a() {return a}, set a(v) {a=v} } }

What about ASI? Adding a unary form of an existing binary operator doesn't introduce any new issues or break/change existing code:

x ={ };

parses as an assignment to x even if = is given a new meaning as a unary operator.

# Peter van der Zee (14 years ago)

As long as we're bikeshedding; none of your examples look as clean to me as the original. If you're dead set on fixing this, try changing the this keyword...

var a; var obj = { get a() {return a}, set a(v) {a=v} };

var obj = { a: null, get a() {return this.a}, set a(v) {this.a=v} };

This looks much cleaner to me than any of the other examples.

(Getters and setters are non-transferable, so they'll always have a context, right?)

# Allen Wirfs-Brock (14 years ago)

The premise was that there are situation where you want to group lexical declarations and then use them in combination to generate a value. The do operator is a way to do this. The fact that you might use a completely different technique to generate a different value isn't very relevant.

More concretely I tried to eliminate superficial elements from my example. The actual use case I had in mind was:

private a; //means: const a = Name.create(); var obj = { @a: null, //or [a] if we stick with that notation for private named property access get a() {return this. at a}, set a(v) {this. at a=v} };

written as:

let obj = do { private a; .{ @a: null,
get a() {return this. at a}, set a(v) {this. at a=v} } };

The original question from another thread is whether we need something like the private declaration inside an object literal

# Rick Waldron (14 years ago)

On Mon, Mar 19, 2012 at 5:42 PM, Peter van der Zee <ecma at qfox.nl> wrote:

As long as we're bikeshedding; none of your examples look as clean to me as the original. If you're dead set on fixing this, try changing the this keyword...

var a; var obj = { get a() {return a}, set a(v) {a=v} };

var obj = { a: null, get a() {return this.a}, set a(v) {this.a=v} };

I just had a "with" flashback. ;)

# Herby Vojčík (14 years ago)

Allen Wirfs-Brock wrote:

(Yet Another Wacky Syntax Idea)

Here is a relatively common coding pattern:

var a; var obj = { get a() {return a}, set a(v) {a=v} };

Often the intent is that the variable a should only be used within the object literal. The block scoping with let and do operator will allow the variable and object literal to be group in this manner:

let obj = do { let a; ({get a() {return a}, set a()v) {a=v} }) }

Unfortunately, the object literal has to be enclosed in parentheses because an expression statement cannot begin with an object literal. The same would be the case if a function expression was involved rather than an object literal.

Parens are annoying because you have to remember to close them and match them if any additional nesting is involved.

With completion reform, do expressions, and broader use of blocks that produce values we should expect to see more situations where an ExpressionStatement needs to start with an object literal or function expression. It would be nice to have some way to do this that does not require parentheses.

Prefix the expression with an unary operator is sufficient to disambiguate such expression but unfortunately all of the existing unary operators perform conversions that would invalidate the value. However, a new unary operator whose semantics was to simply return its operand value would do the job nicely. Both . and = seem like good candidates for such an operator but any operator symbol that is not currently used in a unary form would be a possibility:

let obj = do { let a; ={get a() {return a}, set a(v) {a=v} } }

let obj = do { let a; .{get a() {return a}, set a(v) {a=v} } }

let obj = do { let a; ^{get a() {return a}, //probably my favorite, but that may just be a leakage from my Smalltalk background... set a(v) {a=v} } }

let obj = do { let a; |{get a() {return a}, set a(v) {a=v} } }

let obj = do { let a; /{get a() {return a}, set a(v) {a=v} } }

let obj = do { let a; *{get a() {return a}, set a(v) {a=v} } }

Isn't this already doing it?

let obj = do { let a; 0,{get a() {return a}, set a(v) {a=v} } }

So, just allow comma operator with empty LHS and you've got it:

let obj = do { let a; ,{get a() {return a}, set a(v) {a=v} } }

Not nice. But logical.

# Brendan Eich (14 years ago)

ASI hazards should be avoided. Do not want a prefix unary operator that's also a binary connective.

# Andreas Rossberg (14 years ago)

On 19 March 2012 21:13, Allen Wirfs-Brock <allen at wirfs-brock.com> wrote:

(Yet Another Wacky Syntax Idea)

Here is a relatively common coding pattern:

var a; var obj = { get a() {return a}, set a(v) {a=v} };

Often the intent is that the variable a should only be used within the object literal. The block scoping with let and do operator will allow the variable and object literal to be group in this manner:

let obj = do { let a; ({get a() {return a}, set a()v) {a=v} }) }

Unfortunately, the object literal has to be enclosed in parentheses because an expression statement cannot begin with an object literal. The same would be the case if a function expression was involved rather than an object literal.

How about reusing 'new'?

let obj = do { let a; new { get a() {return a}, set a()v) {a=v} } }

That is, in 11.2.2, semantics of "new NewExpression", simply replace step 4 with

  1. If constructor does not implement the [[Construct]] internal method, return constructor.

(Instead of throwing a TypeError. Probably want to rename 'constructor' there.)

AFAICS, a plain object literal never has [[Construct]], so this should always work. Strictly speaking that is a breaking change, but it seems relatively unlikely that there is much code relying on the current error behaviour. But maybe I'm being naive. :)

# Herby Vojčík (14 years ago)

Andreas Rossberg wrote:

On 19 March 2012 21:13, Allen Wirfs-Brock <allen at wirfs-brock.com <mailto:allen at wirfs-brock.com>> wrote:

(Yet Another Wacky Syntax Idea)

Here is a relatively common coding pattern:

var a;
var obj = {
   get a() {return a},
   set a(v) {a=v}
};

Often the intent is that the variable a should only be used within
the object literal.  The block scoping with let and do operator will
allow the variable and object literal to be group in this manner:

let obj  = do {
    let a;
    ({get a() {return a},
      set a()v) {a=v}
    })
}

Unfortunately, the object literal has to be enclosed in parentheses
because an expression statement cannot begin with an object literal.
  The same would be the case if a function expression was involved
rather than an object literal.

How about reusing 'new'?

let obj = do { let a; new { get a() {return a}, set a()v) {a=v} } }

Or let. We may allow let have object literals in list of assignments and return the last item. So "local var for obj literal" scenario could be:

let obj = do { let a, { get a() {return a}, set a(v) {a=v} } }

And you could write it is one-liner expressions like:

let accumulator = let a = 0, { add (x) { return a+=x; } };

# Axel Rauschmayer (14 years ago)

Would it make sense to cover that use case via some kind of early completion value return (e.g. a break with a completion value). That could then also be used as a return substitute for TCP blocks.

let obj = do { let a; break : {get a() {return a}, set a(v) {a=v} } }

Not exactly a character-saving solution, though.

# Andreas Rossberg (14 years ago)

On 20 March 2012 11:14, Herby Vojčík <herby at mailbox.sk> wrote:

Andreas Rossberg wrote:

How about reusing 'new'?

let obj = do { let a; new { get a() {return a}, set a()v) {a=v} } }

Or let. We may allow let have object literals in list of assignments and return the last item. So "local var for obj literal" scenario could be:

let obj = do { let a, {

  get a() {return a},
  set a(v) {a=v}

} }

I don't know, that seems to over-stretch the meaning of 'let'. It also clashes syntactically with destructuring.

# Herby Vojčík (14 years ago)

Allen Wirfs-Brock wrote:

(Yet Another Wacky Syntax Idea)

Here is a relatively common coding pattern:

var a; var obj = { get a() {return a}, set a(v) {a=v} };

And why not this (specific to object literals, but they are primary concern):

let obj = { let a, // could be 'let a: 0,' to have initial value get a() { return a; } set a(v) { a=v; } }

(read the inner 'let' as 'local' if it feels better)

# Herby Vojčík (14 years ago)

Andreas Rossberg wrote:

On 20 March 2012 11:14, Herby Vojčík <herby at mailbox.sk Or let. We may allow let have object literals in list of assignments and return the last item. So "local var for obj literal" scenario could be:

let obj  = do {
    let a, {

       get a() {return a},
       set a(v) {a=v}
    }
}

I don't know, that seems to over-stretch the meaning of 'let'. It also clashes syntactically with destructuring.

Oh yeah. I forgot destructring. Not using it mentally yet with {}, only with [].

# Allen Wirfs-Brock (14 years ago)

On Mar 20, 2012, at 3:02 AM, Andreas Rossberg wrote:

How about reusing 'new'?

let obj = do { let a; new { get a() {return a}, set a()v) {a=v} } }

That is, in 11.2.2, semantics of "new NewExpression", simply replace step 4 with

  1. If constructor does not implement the [[Construct]] internal method, return constructor.

(Instead of throwing a TypeError. Probably want to rename 'constructor' there.)

AFAICS, a plain object literal never has [[Construct]], so this should always work. Strictly speaking that is a breaking change, but it seems relatively unlikely that there is much code relying on the current error behaviour. But maybe I'm being naive. :)

This would preclude the new <object> proposal which allows any object to be used as the operand for new. The semantics of new <object>(...args) would be approximate:

{let temp = Object.create(<objet>);
 let ctor = <object>.constructor;
 if (typeof ctor == "function") ctor.call(temp, ...args);
 temp

}