Skip to content

lexical shadowing of inst var keys by normal dcls is a hazzard #6

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

Closed
allenwb opened this issue Jan 30, 2018 · 9 comments
Closed

lexical shadowing of inst var keys by normal dcls is a hazzard #6

allenwb opened this issue Jan 30, 2018 · 9 comments

Comments

@allenwb
Copy link
Collaborator

allenwb commented Jan 30, 2018

I initially kind of liked what you were doing with your instance var key lexical bindings. But the first example I tried ran into this:

class Point {
   let x,y;
   constructor (x,y) {
      this->x = x;  //oops
      this->y = y;  //oops again
   }
}
@zenparsing
Copy link
Owner

My intention is to have the scope chain for that example look like this:

[
  { // Class identifier scope
    "Point": class Point
  },
  { // Instance var declaration scope
    "x": uninitialized, 
    "y": uninitialized, 
    "%x": instance_var_key_1,
    "%y": instance_var_key_2
  },
  { // Instance var scope (special lookup rules!)
    "x": instance_var_key_1,
    "y": instance_var_key_2
  },
  { // Function/parameter scope
    "x": argument_0,
    "y": argument_1
  }
]

Using the non-identifiers "%x" and "%y" to implement a separate namespace for use by instance variable member expressions. (Perhaps there's a less hacky way to implement the separate namespace?)

The expression:

this->x

would look up "%x" in the scope chain. It would find an entry for "%x" in the "instance var declaration scope" and use the binding value (an instance variable key) to access the correct instance variable in this.

Does that make sense?

@allenwb
Copy link
Collaborator Author

allenwb commented Jan 30, 2018

OK, I see. You do have a parallel scope chain (whether it is modeled as a separate env record or via non-identifier names amounts to the same thing) at this level of discussion. But the implicit inst var scope around methods still seems to mess up your implicit inst var reference syntax:

constructor (x,y) {
        x = x;  //want to set instance var to  value of parameter
        y = y;

}

Personally, I highly dislike implicit thisqualified references.

@zenparsing
Copy link
Owner

Personally, I highly dislike implicit this qualified references.

I thought you might 😄 .

But I wanted to play with the idea of using let-like declarations modeled after the old class closure pattern:

function C() {
  let x = 0;
  this.getX = () => x;
}

And I think one would expect the "implicit" this semantics from a declaration starting with "let". Of course, you could always use -> if you wanted to.

An advantage of using let is that it is both familiar, (somewhat) intuitive, and it's clear that you're going to need to use something other than . to reference it in a member expression.

@allenwb
Copy link
Collaborator Author

allenwb commented Jan 30, 2018

Well, I also dislike repurposing let in that way. Just like I disliked some very old proposals that used var and function within a class body to define methods and instance properties.

I see if we use existing declarators like let or function they should retain their lexical scoping meaning. That's why I choose private as a strawman keyword.

BTW, In my proposal, I think requiring a . in front of the identifier in private declarations would be fine: private .foo; if we could keep with the .. access qualifier. That would leave room for TypeScript to continue to give private with no . prefix it's own meaning.

@zenparsing
Copy link
Owner

zenparsing commented Feb 1, 2018

The idea is that, while repurposing let is not ideal, it provides us with a model that allows us to have in-body instance variable declarations while avoiding all of the pitfalls that "fields" have run into.

I think we should continue to explore both this avenue and your strawman in parallel. I've added a few examples for the instance variables strawman here.

My experience from coding these examples:

  • For anything other than simple classes, I would expect users to gravitate to either using member expressions of the form a->b or distinguishing their instance variables with a prefix (e.g. _foo).
  • "Private methods" are fairly natural to write as instance variables initialized to an arrow function.
  • "Static private methods" can in general be rewritten as either instance variable arrows (if the method requires access to instance variables) or functions outside of the class (if they do not access instance variables). Compare Domenic's example to this.
  • Friendship is not quite effortless, but is fairly easy.

@allenwb
Copy link
Collaborator Author

allenwb commented Feb 1, 2018

If we repurpose an existing declarator keyword to have a special meaning within a class body, I would prefer using var rather than let. Essentially in class bodies var would mean "instance variable".

Or a new completely new contextual keyword, such as own. (But own probably could create confusion between "own properties" and instance variables.) private would probably be better except that I would like to leave private foo; for typescript. Perhaps I'm convincing my self that (in this context) var would be the best choice.

I really don't like having to add a per instance slot and a per instance closure for each "private method". As I mentioned at last week's meeting. Large classes could have lots of private methods but very simple inst var and own data property state. Adding per instance private method state turns small but algorithmically complex objects into large objects. Increases memory pressure, allocation cost, etc.

But here's a thought. How about:

class Counter extends HTMLElement {
  var x = 0;  //defines an instance variable with a construct time initializer

  ->clicked(){    //defines a private method, shared by all instances
     this->x++;   // always need to explicitly qualify inst var and private method  references
     window.requestAnimationFrame(_=>this->render()); 
   }

  ->render(){this.textContent = this->x.toString()}

  constructor() {
    super();
    this.onclick = args => this->clicked(...args); // or this->clicked.bind(this)
  }

  connectedCallback() { this->render(); }

  static {
    window.customElements.define('num-counter', this);
  }
}

@zenparsing
Copy link
Owner

Essentially in class bodies var would mean "instance variable".

I considered (and actually quite like) var for this purpose. But I worry about the messaging: "you told us to replace all our vars with lets, and now you're telling us to use var again?!".

I'm also curious about expanding this to const...

Large classes could have lots of private methods but very simple inst var and own data property state.

Very true. For instance var closures that are assigned once, only called, and never reified as objects, do you think an implementation could internally share the function?

Interestingly, some developers are already doing this kind of thing to get autobound "methods". I ran across this example today.

@allenwb
Copy link
Collaborator Author

allenwb commented Feb 2, 2018

I considered (and actually quite like) var for this purpose. But I worry about the messaging: "you told us to replace all our vars with lets, and now you're telling us to use var again?!".

If that was the only push back we got, I'd be thrilled. Better to contextually change the meaning of var then let or const.

@zenparsing
Copy link
Owner

Closing in favor of #7

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