Skip to content
This repository has been archived by the owner on Jan 25, 2022. It is now read-only.

Call semantics #85

Open
Alxandr opened this issue Jun 7, 2017 · 11 comments
Open

Call semantics #85

Alxandr opened this issue Jun 7, 2017 · 11 comments

Comments

@Alxandr
Copy link

Alxandr commented Jun 7, 2017

This might have been asked and answered already, but I couldn't find it anywhere, and I figured this might be important to specify, so I thought I'd ask. How would call semantics work with functions you put in private fields? Using the currently proposed grammar, what would be the result of the following?

function getThis() {
  return this;
}

class Test {
  #fn;
  constructor() {
    #fn = getThis;
  }

  doFn1() {
    return #fn();
  }

  doFn2() {
    return this.#fn();
  }
}

const t = new Test();
t.doFn1(); // result?
t.doFn2(); // result?

I would imagine the result of these two should be the same, but I'm unsure whether the logical thing to return is t or undefined.

@ljharb
Copy link
Member

ljharb commented Jun 7, 2017

hmm - good question. I'd expect this.#fn() to have a receiver of the instance, but I'd actually expect #fn() to have a receiver of undefined.

@bakkot
Copy link
Contributor

bakkot commented Jun 7, 2017

As specified, this.#fn and #fn are semantically identical. So both return t.

The potential confusion about the receiver is kind of a downside of allowing the shorthand.

@ljharb
Copy link
Member

ljharb commented Jun 7, 2017

That, at least, is the only other sensible option - but it definitely creates receiver confusion.

@Alxandr
Copy link
Author

Alxandr commented Jun 7, 2017

That, at least, is the only other sensible option - but it definitely creates receiver confusion.

Yeah, that's what I figured as well. And I thought it would be best that we had the discussion sooner rather than later so it can be specified.

Personally, I'm sort of torn between having both of them be undefined, or both of them be t. I could easily see this being defined as a separate fetch and call operation, compiled akin to (0, #fn)().

@littledan
Copy link
Member

This is going to be an even bigger issue when we have private methods. I was assuming that we could use #foo() to call a private method on this.

I agree with @bakkot's analysis above. We've had other potential intuition issues, but eventually came around to thinking that it's clear to thinking that #x would be this.#x in every single way.

@wycats, you have been the big champion of the shorthand. What do you think of this potential intuition issue?

cc @ashleygwilliams

@zenparsing
Copy link
Member

The shorthand introduces some semantics that are (at least initially) surprising for javascript. Receiver-less method invocation forms are fairly common in other languages though, so there's reason to think that the confusion would diminish over time.

On the other hand, I think it's unfortunate that the proposal was put into a position where the shorthand syntax was being used to justify the cost/benefit ratio of the feature. Private fields should stand on their own regardless of sugar. With that in mind, I think it might be better to drop the shorthand from the initial proposal if there's any risk of confusion over the semantics.

@littledan
Copy link
Member

I'm concerned about the confusion issue, but this is only one of several points of confusion that have been brought up by developers in these bug threads. Do we have any particular reason to think that this type of confusion is more common than other types?

@ljharb
Copy link
Member

ljharb commented Jun 16, 2017

I think this one is "two ways to do something", which makes it more problematic.

@bakkot
Copy link
Contributor

bakkot commented Jun 16, 2017

@littledan Mostly I think it's an avoidable confusion. Other potential points of confusion are, it seems, mostly inherent to the design of the proposal, whereas we'd still have private fields if the shorthand were not included.

@Jamesernator
Copy link

I personally think that #foo() should be receiver-less but that methods should be auto-bound.

For example consider this case:

class PriorityQueue {
    #comparator
    #heap
    constructor({ comparator }) {
        #comparator = comparator
        #heap = []
    }

    add(item, priority) {
        ...
        // This should obviously work so I'd propose that methods were
        // auto-bound instead of no-receiver
        #siftUp(this._heap.length - 1)
    }

    #siftUp(index) {
        ...
        // But this probably shouldn't be given a receiver given that
        // #comparator isn't a method, it's just a stored function
        // not a method
        if (#comparator(item, parentItem) === -1) {
             ...
        }
    }
}

@bakkot
Copy link
Contributor

bakkot commented Oct 19, 2017

@Jamesernator, for the moment the shorthand is no longer in the proposal, so this issue doesn't come up.

Separately, given that public methods are already not auto-bound and it's far too late to change that, private ones shouldn't be either.

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

No branches or pull requests

6 participants