Skip to content

AWB alternative proposal sketch #1 #5

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 · 8 comments
Closed

AWB alternative proposal sketch #1 #5

allenwb opened this issue Jan 30, 2018 · 8 comments

Comments

@allenwb
Copy link
Collaborator

allenwb commented Jan 30, 2018

I found it very useful to consider you assumptions/constraints. However, some I agree with; others not so much. I think it will be useful to talk about them relative to another stack-in-the-ground proposal. So here is my first cut at a sketch of one.

Max-min classes 1.1. AWB alternative sketch 1

Basic Concepts

  • a class definition may contain one (or more???) static initializer blocks. Runs as final step of class def evaluation
  • any object may have instance variables that are keyed via private instance variable keys
  • special syntactic form (.. in following example) for accessing an instance variable of an object
  • no reflection on instance variables or instance variable keys
  • lexically scoped private declarations introduce new instance variable keys into a parallel scope (does not shadow non-private name)
  • no field/instance variable/non-method data property declarations, properties and instance variables created via assignment. Just like today
  • within a class definition, instance properties and instance variables are normally defined in constructor; class (ie static) properties or instance variables are normally defined in a static initializer block.

Example

class Point {
   private x,y;   // used for instance variables
   private instantiationCount;  //used for a class instance variable
   
   constructor (x,y) {
      this..x = x;
      this..y = y;
      Point..instanceCount++;
   }
   
   get x() {return this..x}
   get y() {return this..y}

   static {
      Point..instantiationCount = 0;
      Point.origin = Object.freeze(new Point(0, 0));
      Object.defineProperty(Point, "prototype", {configurable: false, writable: false});
      Object.freeze(Point.prototype);
   }
}
@zenparsing
Copy link
Owner

Sounds like our two proposals have a lot in common, which is great!

A semantic question: what happens if I don't "initialize" an instance variable before the class constructor returns?

class Point {
  private x, y;
  constructor(x,y) {
      this..x = x;
      // I forgot to initialize "y"
   }
}

@allenwb
Copy link
Collaborator Author

allenwb commented Jan 30, 2018

In my proposal, private doesn't declare an instance variable, it simply defines an instance variable key. Actual instance variable are only created via assignment, pretty much just like a property (except for reification of the meta operations).

Note, that this means that the shape of an object's instance var state is not (easily) determined before or at object allocation time. This used to be a requirement but my understanding in that the current class fields proposal dropped that requirement so I assume that we can also ignore it as a requirement.

Also, in my proposal there is no particular reason that a private declaration has to be contained within a class body. If allowed outside of class body it would support "friend" access to instance vars.

@zenparsing
Copy link
Owner

Note, that this means that the shape of an object's instance var state is not (easily) determined before or at object allocation time. This used to be a requirement but my understanding in that the current class fields proposal dropped that requirement so I assume that we can also ignore it as a requirement.

I think so (and I like the easy friendship patterns), but there's one issue that I'm curious about. Suppose we have the following:

class C {
  private x;
  constructor(x) { this..x = x }
  get x() { return this..x }
  doAssignment() { this..x = 12345 }
}

And I do this:

let obj = {};
// Attach C prototype methods to this object
Object.defineProperties(obj, Object.getOwnPropertyDescriptors(C.prototype));
obj.doAssignment();
console.log(obj.x); // 12345?

It would seem to be the case that I have an object obj that might pass a brand check for C, but that wasn't created by C.

@allenwb
Copy link
Collaborator Author

allenwb commented Jan 30, 2018

Good point. With dynamically added instance variables you can't ways depend upon using the existence of an instance variable as a brand.

BTW, I think what you show is probably a fine use case for inst vars, it just isn't branded and/or sealed.

What you probably would have to do is make sure that you do your branding setup in the constructor. So one way:

class C {
  private x, brand;
  constructor(x) { 
      this..brand = true;
      this..x = x }
  get x() {return this..brand, this..x }  //assumes accessing a missing instance var throws
  doAssignment() { this..brand, this..x = 12345 }
}

or assuming that Object.seal prevents adding additional instance vars:

class C {
  private x;
  constructor(x) { 
     this..x = x;
     Object.seal(this)}
  get x() {return this..x }  //assumes accessing a missing instance var throws
  doAssignment() { this..x = 12345 } // assumes storing into a missing instance var throws
}

But, sealing in the constructor is problematic if you might subclass it. I have a possible solution to that problem, but I was going to save it for my classes 1.2 addendum.

@zenparsing
Copy link
Owner

How would you rewrite "private methods" using this proposal? My guess is that the options are:

  • Put the instance var key declaration into an outer scope and define all helpers outside of the class
  • Create additional instance var key declarations in the class body and use them to hold instance-level closures.

@allenwb
Copy link
Collaborator Author

allenwb commented Feb 1, 2018

  • Put the instance var key declaration into an outer scope and define all helpers outside of the class

alternatively, Declare lexically scoped variables outside of the class and class body static initializer block to initialize those variables using function expressions that can reference instvar keys declared within class body.

  • Create additional instance var key declarations in the class body and use them to hold instance-level closures.

sure, but I typically wouldn't want to do this because I don't want to add redundent inst var state to each instance.

Any of these technique would work with my classes 1.1 proposal. But, they are admittedly kind of ugly. That is why my classes 1.2 proposal would add class body lexical function (at least) declaration possibly a syntactic extension to avoid having to invoke them as helper.call(this, ...)

@zenparsing
Copy link
Owner

That is why my classes 1.2 proposal would add class body lexical function (at least) declaration possibly a syntactic extension to avoid having to invoke them as helper.call(this, ...)

Reminds me of the presentation I gave in Paris, using the old :: operator idea for this-binding/this-calling.

It looks like all the momentum is behind the pipeline operator for chaining, but that might work for this proposal as well:

class C {
  private .foo;

  function init(instance) {
    return instance..foo = '123';
  }

  constructor() {
    this |> init;
  }
}

Going back a bit, this discussion has clarified for me a constraint that we've assumed in the past. In previous proposals, we've assumed a distinction between adding an instance variable to an object and setting an instance variable. A "field" declaration has been a convenient way to express that distinction.

@zenparsing
Copy link
Owner

Closing in preference to #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