Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

The unary form feels like it's really a binary operator in disguise. #8

Closed
aickin opened this issue May 16, 2015 · 1 comment
Closed

Comments

@aickin
Copy link

aickin commented May 16, 2015

So, two disclaimers to start:

  1. I think this is a really cool proposal, and I look forward to some form of it making it into ES engines.
  2. I may just misunderstand this proposal, as I'm not a JS language feature or spec expert.

That being said, I find the unary version really unintuitive. Given the following code snippet:

// snippet A
let boundLog1 = ::console.log;

let unboundLog = console.log;
let boundLog2 = ::unboundLog;

boundLog1("foo"); // "foo"
boundLog2("bar"); // "bar"? TypeError: Illegal Invocation?

What does the last line do? I see two scenarios: (1) throws an error or (2) logs "bar".

As I read the spec (which is, again, from someone who is naive about the ES formal spec), I believe it's (1). I think that since unboundLog isn't a Reference, the :: operator doesn't bind unboundLog to a base, and some form of error is thrown because log is not bound to console. Is that correct? If it is, I'd contend that the unary :: is actually a binary operator being used in prefix notation; it always needs two operands (an object and a function) to be useful. And while prefix notation might be able to work, it seems confusing in a language where binary operators almost always occur in between operands. I would much rather have such a function bind operator that occurred between the two operands, but I think that means you'd have to use something other than ::, since you want to reserve that for the current binary form.

If, on the other hand, the last line does in fact log "bar", I think that's odder from a programmer's perspective. We've been taught for years that ES functions that happen to be members of an object have no record of the object they came from and have to be bound to that object to access this, but now we find that a variable holding just a ref to such a function does somehow know which object it came from. Imagine the following code:

// snippet B
let obj1 = { x: "foo"};
let obj2 = { x: "bar"};

obj1.logX = obj2.logX = function() { console.log(this.x);}
obj1.logX(); // "foo"
obj2.logX(); // "bar"

let unboundFn1 = obj1.logX;
let unboundFn2 = obj2.logX;

console.log(unboundFn1 === unboundFn2); // "true"

let boundFn1 = ::unboundFn1;
let boundFn2 = ::unboundFn2;

boundFn1(); // "foo"???
boundFn2(); // "bar"???

If this second scenario is correct, then boundFn1 and boundFn2 would produce different results, even though unboundFn1 and unboundFn2 are exactly (triple equals) equivalent. That seems extremely confusing.

So: which, if any, of these interpretations are correct of the behavior of snippet A? And if the first scenario is correct, do you agree that the unary :: isn't really unary?

@zenparsing
Copy link
Member

Hi @aickin - thanks for the feedback!

As currently written, applying the unary form to something other than a reference will result in undefined being used as the bound this value.

However, if we go with #4 (which is implemented in both esdown and babel at the moment), then attempting to apply the unary form to a non-reference will result in a static error. I think that's a good idea and will update the proposal accordingly at some point.

So:

::unboundLog; // SyntaxError

Your larger question is: does it makes sense to use a reference as the single operand of a unary operator like this?

Well, there is existing precedent in the language. For example:

delete x.y;
++x.y;
--x.y;

In each case we can take a reference as operand, and it seems intuitive enough.

What do you think?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants