The current Harmony classes proposal 
http://wiki.ecmascript.org/doku.php?id=harmony:classes includes the concept of 
private instance members and syntax for defining them.  While it presents a 
syntax for accessing them (eg, private(foo).bar accesses the private 'bar' 
member of the object that is the value of foo) there does not yet appear to be 
consensus acceptance of this access syntax. There also appears to be a number 
of still unresolved semantic issues WRT such private instance members.  As an 
alternative, the classes proposal says that  "a pattern composing private names 
with classes can satisfy" the requirements for private instance members. 
However, so far, we have no concrete proposals on what these patterns might be. 
 This is a first cut at proposing such patterns.


The current versions of the private names proposal 
http://wiki.ecmascript.org/doku.php?id=harmony:private_name_objects  simply 
exposes a constructor for creating unique values can be be used as property 
keys:

    const key = Name.create();

Those values can be used to define and access object properties:

    let obj = {};
   print(obj.key);     // "undefined"
   obj[key]="some private state";   //create a property with a private name key
   print(obj.key);    // still "undefined"
   print(obj["key"]); //"undefined"
   print(obj[key]);   // "some private state"

In the above example the property created by using the private name value is 
essentially an instance private member of  obj.  In general, various levels of 
visibility (instance private, class private, friends, module private, etc.) can 
be achieved simply by using lexical scoping to control access to the variable 
that contain private key values. 

For example:

   function ObjWithInstancePrivateMember(secret) {
        const s = Name.create();
        this[s]= secret;
        this.method = function () {
            return doSomething(this[s]);
        }
   }

   const priv = Name.create();
   function ObjWithClassPrivateMember(secret) {
        const s = Name.create();
        this[priv]= secret;
        this.method = function (another) {
            return doSomethingTogether(this[priv], another[priv]);
        }
   }

Note that  [ ] notation with an expression that evaluates to a private name 
value must be used to access such private members.  There have been various 
proposals such as http://wiki.ecmascript.org/doku.php?id=strawman:names and  
http://wiki.ecmascript.org/doku.php?id=strawman:private_names to allow dotted 
property access to be used with private name keys.  However, these either had 
technical problems or their semantics were rejected by community discussion on 
this list.  The issues raised in response to those proposals are likely to 
recur for any private member access syntax that uses dot notation.

A weakness with using private names object to define private members is that 
currently there is no way to declaratively define objects with such members. 
Consider the following definition of a Point abstraction using extended object 
literals and a simple naming convention to identify "private" members:

const Point = {
     //private members
     __x: 0,
     __y: 0, 
     __validate(x,y) { return typeof x == 'number' && typeof y = 'number'},
     //public members
     new(x,y) {
          if (!this.__validate(x,y)) throw "invalid";
          return this <| {
                  __x: x,
                  __y: y
                 }
      };
     add(anotherPoint) {
           return this.new(this.__x+another.__x, this.__y+another.__y)
     }
}

using private name objects this would have to be written as:

const __x=Name.create();
const __y=Name.create();
const __validate=Name.create();
const Point = {
     //public members
     new(x,y) {
          if (!this[__validate](x,y)) throw "invalid";
          let obj = this <| { };
         obj[__x] = x;
         obj[__y] = y;
         return obj;
           };
     add(anotherPoint) {
           return this.new(this[__x]+another[__x], this[__y]+another[__y])
     }
}
 //private members for Point
Point[__x] = 0;
Point[__y] = 0,;
Point[ __validate] = function (x,y) { return typeof x == 'number' && tyupeof y 
= 'number'};

The biggest issue with this rewrite is it loose the declarative definition and 
replaces it with an hybrid that has some declarative parts and some imperative 
parts. This breaks the logical encapsulation of the definition of the Point 
abstraction making it harder to be understand and manipulated by both humans 
and mechanical tools.  The reason this is necessary is that object literal 
notation currently has no way to express that a definition of a property uses a 
name that needs be interpreted as a reference to a lexical variable whose value 
is the actual private name object.  Fixes these requires an additional 
extension to object literals.

One possibility, is to use a prefix keyword to each property definition that 
uses a private key value.  For example:

const __x=Name.create();
const __y=Name.create();
const __validate=Name.create();
 Point = {
     //private members
     private __x: 0,
     private __y: 0, 
     private __validate(x,y) { return typeof x == 'number' && typeof y = 
'number'},
     //public members
     new(x,y) {
          if (!this[__validate](x,y)) throw "invalid";
          return this <| {
                  private __x: x,
                  private __y: y
                 }
      };
     add(anotherPoint) {
           return this.new(this[__x]+another[__x], this[__y]+another[__y])
     }
}

Note that the separate definition of the private names is still required. In 
the above example, prefixing a property definition with 'private' means that 
the identifier in the property name position is evaluated as a lexical 
reference and its value is used as key of the property that is being defined.  
(If it is not a private name object, ToString of the value is used as the key). 
There are a couple potential issues with this approach.  The 'private' keyword 
is very noticeable, but its meaning is different from what might be expected 
from readers familiar with widely used languages. It particular, it does not 
control the actual  accessibility of the property.  It only means that the 
property key (may) be a private name value.  The actual accessibility of the 
property is determined by the visibility of the private name value.  Another 
potential issue is that is precludes the use of 'private' for other purposes. 
For example, a possible short hand for:

const __x=Name.create();
const __y=Name.create();
const __validate=Name.create();

might be:

private __x;
private  __y;
private __validate;

and if this was available, it might be be useful to allow such definitions to 
occur inside an object literal so that private name object definitions could be 
logically bundled in the object declaration that uses them. The use of 
'private'  as a property prefix might preclude this and other usages of 
'private'.

Another alternative that avoids using the 'private' prefix is to allow the 
property name in a property definition to be enclosed with brackets:

const __x=Name.create();
const __y=Name.create();
const __validate=Name.create();
 Point = {
     //private members
     [__x]: 0,
     [ __y]: 0, 
     [__validate](x,y) { return typeof x == 'number' && typeof y = 'number'},
     //public members
     new(x,y) {
          if (!this[__validate](x,y)) throw "invalid";
          return this <| {
                  [__x]: x,
                  [__y]: y
                 }
      };
     add(anotherPoint) {
           return this.new(this[__x]+another[__x], this[__y]+another[__y])
     }
}

The meaning is the same as the private prefix, the identifier enclosed in 
brackets is evaluated as a lexical reference and its value is used as the key 
of the property that is being defined.  Using brackets in this manner would 
enforce the fact that private name keyed properties can only be accessed by [ ] 
property access syntax.

Of course some people may object to this requirement, that bracket access must 
be used to access private state properties.  One possible objection is that [ ] 
suggests array access or more generally computed property access where the 
actual property accessed is likely to vary between different executions of an 
expression containing them.  A fix for this could be to use a different 
notation for private name based property access.  For example, obj@foo could be 
defined to mean the same thing as obj[foo] if foo's value is a private name 
object.  If that notation was used for property accesses it could also be used 
for property declarations.  For example:

const __x=Name.create();
const __y=Name.create();
const __validate=Name.create();
 Point = {
     //private members
    @__x: 0,
    @__y: 0, 
    @__validate(x,y) { return typeof x == 'number' && typeof y = 'number'},
     //public members
     new(x,y) {
          if (!this@validate(x,y)) throw "invalid";
          return this <| {
                  @__x: x,
                  @__y: y
                 }
      };
     add(anotherPoint) {
           return this.new(this@__x+another@__x, this@__y+another@__y)
     }
}

Each of these approaches seem plausible.  For a side-by-side comparison of the 
above example using the alternatives see 
http://wiki.ecmascript.org/lib/exe/fetch.php?id=harmony%3Aprivate_name_objects&cache=cache&media=harmony:private-name-alternatives.pdf
 .  I'm interested in feedback on the alternatives.

I've only used object literal in these examples, however the same alternatives 
should be equally applicable to class declarations.

Allen






_______________________________________________
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss

Reply via email to