Private Names and Methods

# Kevin Smith (13 years ago)

Let's say that we have a class with two methods, and we divide the body of each method into abstract "regions":

M1() {
  <A>
  <B>
  <C>
}

M2() {
  <D>
  <C>
  <E>
}

Both M1 and M2 share the region <C>. In a traditional class-based

language, we would refactor <C> into an abstract operation using a private

method:

M1() {
  <A>
  <B>
  this.M3();
}

M2() {
  <D>
  this.M3();
  <E>
}

private M3() {
  <C>
}

In a traditional class-based language, the refactored code will:

  1. provide an identical public interface, and
  2. be functionally identical

These two guarantees allow us to separate interface from implementation, and to localize the implementation strategy.

Will private methods, implemented with private name objects, provide us with the same guarantees? Let's refactor again, this time using a private name for the new method:

// Assume that there exists a "const M3 = Name.create();" somewhere

above

M1() {
  <A>
  <B>
  this[M3]();
}

M2() {
  <D>
  this[M3]();
  <E>
}

M3 { <C> }

Since M3 is not visible to any code that does not also have a reference to the M3 private name, we can say that this refactoring provides the same public interface. But is it functionally identical?

Let's assume that prior to the refactoring, M1 and M2 were generic methods. That is, they reference only properties of |this| which correspond to some subset S of the public interface of the class. Any other object which implements S can also be used with M1 and M2 (like the generic methods defined on Array.prototype).

After the refactoring shown above, there's no possibility that M1 and M2 can be generic, since they access a privately named property of |this| which by definition is not a part of the public interface of the class. So in this sense, the refactored code is not functionally identical to the original code.

If we wanted to preserve identical functionality, we could define M3 as a regular function and call it using the call method of Function.prototype:

// Somewhere *before* the class definition we have:
function M3() {
  <C>
}

...

M1() {
  <A>
  <B>
  M3.call(this);
}

M2() {
  <D>
  M3.call(this);
  <E>
}

The above strategy works, but (a) explicitly calling M3 via call() is awkward, and (b) it defeats the purpose of an elegant class syntax if we must refactor code into functions outside of the class definition.

Thoughts?

# Kevin Smith (13 years ago)

For some reason, I wasn't able to send this as an email, so here it is as a blog post:

blog.khs4473.com/2012/04/private-names-and-methods.html

Summary: I take a look at the possibility of using private methods for internal refactoring within a class and point out some problems.

Thoughts?

# Kevin Smith (13 years ago)

(I seem to have some trouble sending this one through - let's see if it works this time...)

Let's say that we have a class with two methods, and we divide the body of each method into abstract "regions":

M1() {
  <A>
  <B>
  <C>
}

M2() {
  <D>
  <C>
  <E>
}

Both M1 and M2 share the region <C>. In a traditional class-based

language, we would refactor <C> into an abstract operation using a private

method:

M1() {
  <A>
  <B>
  this.M3();
}

M2() {
  <D>
  this.M3();
  <E>
}

private M3() {
  <C>
}

In a traditional class-based language, the refactored code will:

  1. provide an identical public interface, and
  2. be functionally identical

These two guarantees allow us to separate interface from implementation, and to localize the implementation strategy.

Will private methods, implemented with private name objects, provide us with the same guarantees? Let's refactor again, this time using a private name for the new method:

// Assume that there exists a "const M3 = Name.create();" somewhere

above

M1() {
  <A>
  <B>
  this[M3]();
}

M2() {
  <D>
  this[M3]();
  <E>
}

M3 { <C> }

Since M3 is not visible to any code that does not also have a reference to the M3 private name, we can say that this refactoring provides the same public interface. But is it functionally identical?

Let's assume that prior to the refactoring, M1 and M2 were generic methods. That is, they reference only properties of |this| which correspond to some subset S of the public interface of the class. Any other object which implements S can also be used with M1 and M2 (like the generic methods defined on Array.prototype).

After the refactoring shown above, there's no possibility that M1 and M2 can be generic, since they access a privately named property of |this| which by definition is not a part of the public interface of the class. So in this sense, the refactored code is not functionally identical to the original code.

If we wanted to preserve identical functionality, we could define M3 as a regular function and call it using the call method of Function.prototype:

// Somewhere *before* the class definition we have:
function M3() {
  <C>
}

...

M1() {
  <A>
  <B>
  M3.call(this);
}

M2() {
  <D>
  M3.call(this);
  <E>
}

The above strategy works, but (a) explicitly calling M3 via call() is awkward, and (b) it defeats the purpose of an elegant class syntax if we must refactor code into functions outside of the class definition.

# Axel Rauschmayer (13 years ago)

Aren’t you possibly overthinking the problem? Generic methods should normally only access public methods. Should you ever have different needs, you would need to come up with a more elaborate solution such as clients having to import both the generic method and the private name it uses.

On the other hand, private names are incredibly flexible in that you can easily implement any kind of “friend” (à la C++) scheme, simply by passing the name to all parties that should know about it. That means that you can, say, implement a “layer” where one fragment (=set of methods) per class among a set of classes communicates via a private protocol.

# Kevin Smith (13 years ago)

On the other hand, private names are incredibly flexible in that you can easily implement any kind of “friend” (à la C++) scheme, simply by passing the name to all parties that should know about it. That means that you can, say, implement a “layer” where one fragment (=set of methods) per class among a set of classes communicates via a private protocol.

I'm not addressing the usefulness of private names for other situations. I am stating that they are not appropriate for refactoring out common code within a class.

# Axel Rauschmayer (13 years ago)

Agreed. But as far as I can see, such common code should always be able to take its private name with it.

For example:

let mixin = do { let privateName = name.create(); ({ privateName { ... }, publicMethod(...) { ... thisprivateName ... }, }); };

Even if defined outside, within a module, the private name should always be within reach.

# Kevin Smith (13 years ago)

Even if defined outside, within a module, the private name should always be within reach.

But then it's part of the external interface of the class (or object or whatever). You don't want to have to increase the surface area of your abstraction just to refactor out common implementation code.

# Brendan Eich (13 years ago)

Kevin Smith wrote:

Even if defined outside, within a module, the private name should
always be within reach.

But then it's part of the external interface of the class (or object or whatever). You don't want to have to increase the surface area of your abstraction just to refactor out common implementation code.

No, in the example Axel showed, the private name is block-scoped and does not leak. Same goes for a module: you'd need to export the binding. Or so I've thought -- do you see another way the name could leak?

# Allen Wirfs-Brock (13 years ago)

On Apr 4, 2012, at 6:27 PM, Kevin Smith wrote:

... Will private methods, implemented with private name objects, provide us with the same guarantees? Let's refactor again, this time using a private name for the new method:

// Assume that there exists a "const M3 = Name.create();" somewhere above

M1() {
  <A>
  <B>
  this[M3]();
}

M2() {
  <D>
  this[M3]();
  <E>
}

M3 { <C> }

Since M3 is not visible to any code that does not also have a reference to the M3 private name, we can say that this refactoring provides the same public interface. But is it functionally identical?

Let's assume that prior to the refactoring, M1 and M2 were generic methods. That is, they reference only properties of |this| which correspond to some subset S of the public interface of the class. Any other object which implements S can also be used with M1 and M2 (like the generic methods defined on Array.prototype).

After the refactoring shown above, there's no possibility that M1 and M2 can be generic, since they access a privately named property of |this| which by definition is not a part of the public interface of the class. So in this sense, the refactored code is not functionally identical to the original code.

I don't think this is really a problem or if it is, that the problem isn' specific to private name based procedural decomposition. Consider such a "generic method". How can you actually use such a method with "any other object that implements S". In ES there are basically two ways that are pretty much semantically equivalent. You access the M1 function value and install it as a method (with a name of your choosing) in the other object and invoke it as a method. Or, you use Function.prototype.call (or apply) to invoke the functions with the other object as the this value. Either technique only works if M1 is completely self contained or is only dependent upon the S interface. The manner in which it is not self contained is really important. Any mechanism that depends upon dynamic coupling to state or behavior that is not part of S breaks the generic-ness of M1. A reference to a Java-like private declaration would still have the same problem (at least for state access) as the "other object" would not have the proper shape to support that reference.

If you want to refactor the implementation of M1 you must keep it "self contained" in order to keep it generic. A captured lexical reference to a helper function does that.

If we wanted to preserve identical functionality, we could define M3 as a regular function and call it using the call method of Function.prototype:

// Somewhere *before* the class definition we have:
function M3() {
  <C>
}

...

M1() {
  <A>
  <B>
  M3.call(this);
}

M2() {
  <D>
  M3.call(this);
  <E>
}

The above strategy works, but (a) explicitly calling M3 via call() is awkward, and (b) it defeats the purpose of an elegant class syntax if we must refactor code into functions outside of the class definition.

You don't have to use call, you can just define an additional argument for M3 (which should perhaps be called F3 as it isn't actually a method). A principle of refactoring is that valid refactorings are based upon well defined rules that preserve the semantics of the original code across the transformation. The transformations/rule sets are typically given meaningful names. In this case, the refactoring you want to perform is not "Extract Method" but something else, perhaps called "Extract helper function from method". Just like "extract method", the refactoring would turn any references to method arguments or local declarations (outside of the extracted fragment) into explicit parameters of the extracted function. However, if the fragment contained any "this" references, it must also provide an explicit parameter for transmitting the this value. The proper refactoring would be:

M1() {
  <A>
  <B>
  F3(this, /*any local values referenced by <C>*/);
}

M2() {
  <D>
  F3(this, /*any local values referenced by <C>*/);
  <E>
}

// ES scoping rules permit this to appear either beforeor after the class definition: function F3(baseThis, /* any free variables in <C>*/ ) { <C with all occurrences of "this" replaced with "baseThis"> }

# Kevin Smith (13 years ago)

No, in the example Axel showed, the private name is block-scoped and does not leak. Same goes for a module: you'd need to export the binding. Or so I've thought -- do you see another way the name could leak?

No - I just misunderstood Axel's point. IIUC, he's showing how a privately named method can always "travel along" with the methods that require it, by bundling those methods together in a mixin object.

# Kevin Smith (13 years ago)

I completely agree with everything you wrote, and with the transformations necessary to correctly perform the refactoring I'm talking about.

The transformation is still too low-level though. In order to perform it, we have to switch from thinking in terms of methods to functions and back again. It would be preferable if the mismatch weren't so great.

Would something like Dave's bind operator be applicable here?

M1() {
  ...
  this::helperMethod();
  ...
}
# Allen Wirfs-Brock (13 years ago)

On Apr 5, 2012, at 12:48 PM, Kevin Smith wrote:

Hi Allen,

I completely agree with everything you wrote, and with the transformations necessary to correctly perform the refactoring I'm talking about.

The transformation is still too low-level though. In order to perform it, we have to switch from thinking in terms of methods to functions and back again. It would be preferable if the mismatch weren't so great.

Would something like Dave's bind operator be applicable here?

M1() {
  ...
  this::helperMethod();
  ...
}

Not really. For that to work helperMethod would still have to be declared via a in scope function declaration and rather than as a method in the same class or object literal as M1. In that case, it really isn't a method and shouldn't be named as such or use a this reference. All the bind (or call/apply) is saving is the renaming of any "this" references in the original <C> fragment. But you should want to do that renaming because the helper is no longer a method so making it a function that references "this" would just be a likely source of confusion concerning the intended use of the function

# Brendan Eich (13 years ago)

Allen Wirfs-Brock wrote:

But you should want to do that renaming because the helper is no longer a method so making it a function that references "this" would just be a likely source of confusion concerning the intended use of the function

IOW, OOP-uber-alles does not work and is not necessary in JS. This is considered a virtue by some, a vice by others. Discuss!

# Herby Vojčík (13 years ago)

Brendan Eich wrote:

Allen Wirfs-Brock wrote:

But you should want to do that renaming because the helper is no longer a method so making it a function that references "this" would just be a likely source of confusion concerning the intended use of the function

IOW, OOP-uber-alles does not work and is not necessary in JS. This is considered a virtue by some, a vice by others. Discuss!

Yes, I agree, +1. If something is "method-like" I do not see why not to use this/call/apply. It may as well be "helper pluggable-function" (there may be a bunch of them an d I can call that bunch a "dynamic mixin"), there is no law that "helper" can only be a function. And what's not object-oriented there? They are still objects (as per "X is object iff X has identity, state and behaviour" except behaviour is not necessarily declared statically and at one place).

# Kevin Smith (13 years ago)

IOW, OOP-uber-alles does not work and is not necessary in JS.

Yep - I'm sensing that.

Actually, I'm trying to push the "class" idea to see where it breaks down, and I think my original point still stands: "methods with private names" and OOP-style "private methods" are not the same thing.

More to the point: it seems that given the inability of privately named methods to fully emulate OOP-style privacy, it would seem unlikely that we'd ever want a "private" method modifier, as in:

private M3() { ... }

It would just be too confusing.

# Allen Wirfs-Brock (13 years ago)

On Apr 6, 2012, at 5:32 AM, Kevin Smith wrote:

IOW, OOP-uber-alles does not work and is not necessary in JS.

Yep - I'm sensing that.

Exclusive OO languages work just fine as do exclusives functional languages. The pros/cons of each approach and where each may be a better fit could be the topic of a interesting, not not particularly essential conversations.

The issues we are addressing in this thread and in general as ES programmers is how to be most effective in a hybrid language that offers both OO and functional constructs. We can try to stick to a pure OO subset or a pure functional subset, but in practice we are typically either forced or find benefit in using both styles within most programs.

Actually, I'm trying to push the "class" idea to see where it breaks down, and I think my original point still stands: "methods with private names" and OOP-style "private methods" are not the same thing.

A point that I've been trying to consistently make is that "method" is an OO concept. It is is a behavioral member of an object and is invoked in a specific manner that emphasizes the object. A method isn't just any function whose first formal parameter is named "this" or"self". If a "method with private names" is not "OOP-style" then we should be calling it a "method" and shouldn't be talking about other OOP characters such as invocations that explicitly distinguished "this" value.

However, I don't believe your original point is at all correct.

To start, it is worth noting that "private method" is not even a concept that existed in the languages that the OOP-style emerged from. Originally all methods were "public". Over-time emerged recognition of the desirability of clearly labeling the public protocol (ie interface) of an object as distinct from its "private" implementation details (including the private procedural decomposition of public methods). This was originally done via tools and documentation conventions, not via language enforcement. Second and third generation OOP languages (eg, Eiffel, Java) explored various linguistic mechanisms to control from where "private methods" could be observed and invoked. But they didn't change anything about the method-ness of "private method" . They were still tightly associated with objects (via class definitions) and their invocation still involves a distinguished "this" value. The only thing that "private" changed was which source code regions of a program had visibility of the existence of "private methods".

This is exactly what "private name" properties do in ES. They can be used to control the visibility of a property of an object. That's all. If you accept that a function-valued property of a ES object directly corresponds to a OO method (and my position is that some do and other don't) then whether the property has a private or string name should have to relevance to that correspondence.

More to the point: it seems that given the inability of privately named methods to fully emulate OOP-style privacy, it would seem unlikely that we'd ever want a "private" method modifier, as in:

But I don't think you've demonstrated that inability (or defined what you mean by "OOP-style privacy").

You original post related to a "generic method" that you wished to dynamically exist as a method on multiple, otherwise unrelated, objects. This isn't even something that could be accomplished in most OOP languages without resorting to the metaprogramming/reflection layers of the language. I think I demonstrated in my first reply that if you did that in an exclusively OO language you would run into the same issues that you think you see in ES. (In other words, have you tried to do what you are described in Java?).

private M3() { ... }

It would just be too confusing.

You would need to define your intended semantics, but assume this just means that M3 is a private name scoped to a class definition then I don't see why this would add additional points of confusions.

I think your confusion probably comes from focusing too closely on the intersection between the OOP and functions aspects of ES. This can indeed be potentially confusing territory. I think for new features we should try to keep those aspects of ES as distinct as possible. For example, that is why I'm so opposed to any sort of optional "this" formal parameter for arrow functions.

# Allen Wirfs-Brock (13 years ago)

On Apr 6, 2012, at 1:19 AM, Herby Vojčík wrote:

Brendan Eich wrote:

Allen Wirfs-Brock wrote:

But you should want to do that renaming because the helper is no longer a method so making it a function that references "this" would just be a likely source of confusion concerning the intended use of the function

IOW, OOP-uber-alles does not work and is not necessary in JS. This is considered a virtue by some, a vice by others. Discuss!

Yes, I agree, +1. If something is "method-like" I do not see why not to use this/call/apply. It may as well be "helper pluggable-function" (there may be a bunch of them an d I can call that bunch a "dynamic mixin"), there is no law that "helper" can only be a function. And what's not object-oriented there? They are still objects (as per "X is object iff X has identity, state and behaviour" except behaviour is not necessarily declared statically and at one place).

Keven's original problem statement imposed the requirement that refactoring a method to use a "helper" retained the ability to individually move the method to a different object without moving any associated "private methods". Such a refactoring can only be accomplished by decomposing the method into lexically bound helper functions rather than dynamically (invocation) bound helper methods. There is nothing wrong with the sort of decomposition you are referring to. It just doesn't work for Kevin's problem statement (in any language).

# Kevin Smith (13 years ago)

I think your confusion probably comes from focusing too closely on the intersection between the OOP and functions aspects of ES.

I don't think I'm confused, but I understand your points. Thanks for the discussion.

# Axel Rauschmayer (13 years ago)

As Allen mentioned, I think it would really help if you could show, say, real Java (or C# or C++) code and explain what it can do that JavaScript + private names can’t.

Multiple inheritance is where things become interesting. In order to implement some kind of mixin or trait mechanism for JavaScript, you would need the ability to copy private members.

# Brendan Eich (13 years ago)

Axel Rauschmayer wrote:

Multiple inheritance is where things become interesting. In order to implement some kind of mixin or trait mechanism for JavaScript, you would need the ability to copy private members.

Which .{ can do. The argument is that this is safe against unintended name leaks because while the LHS (mixin-derived) is an expression, the RHS (mixin-base) is a literal.

Is having only the literal-RHS-only special form too restrictive? It could be wrapped in an API usable from downrev script, but this means implementors of mixins must use ES6 even though consumers can use pre-ES6.

# Russell Leggett (13 years ago)

On Fri, Apr 6, 2012 at 1:42 PM, Brendan Eich <brendan at mozilla.org> wrote:

Axel Rauschmayer wrote:

Multiple inheritance is where things become interesting. In order to implement some kind of mixin or trait mechanism for JavaScript, you would need the ability to copy private members.

Which .{ can do. The argument is that this is safe against unintended name leaks because while the LHS (mixin-derived) is an expression, the RHS (mixin-base) is a literal.

Is having only the literal-RHS-only special form too restrictive? It could be wrapped in an API usable from downrev script, but this means implementors of mixins must use ES6 even though consumers can use pre-ES6.

I feel like I'm probably not the first to suggest this, but what about a spread operator for objects?

foo.{ ...myMixin }

It would not only work for something like this, but also in regular object literals to mixin.

let foo = { a:1, b:2, ...myMixin}

And would result in copying all of the properties from myMixin into the object literal.

# Brendan Eich (13 years ago)

Russell Leggett wrote:

And would result in copying all of the properties from myMixin into the object literal.

Including the private-named properties? That would be bad for integrity: Alice can't give Bob an object-as-capability where the private names stay private. Bob can suck them out via this hypothetical spread-in-braces and then abuse them (e.g. to brand a trojan object and fool Carol into thinking it was from Alice).

# Brendan Eich (13 years ago)

Russell Leggett wrote:

And would result in copying all of the properties from myMixin into the object literal.

Including the private-named properties? That would be bad for integrity: Alice can't give Bob an object-as-capability where the private names stay private. Bob can extract them via this hypothetical spread-in-braces and then abuse them (e.g. to brand a counterfeit object and fool Carol into thinking it was from Alice).

# Kevin Smith (13 years ago)

As Allen mentioned, I think it would really help if you could show, say, real Java (or C# or C++) code and explain what it can do that JavaScript + private names can’t.

(Sorry for that double-post, BTW)

I can't : )

It's an abstract guarantee. Private methods (in Java let's say) give me the guarantee that I can refactor at will with them without any externally visible changes. Privately named methods in JS do not.

Look at it this way: if I have a method that looks like this:

private helperMethod() { ... }

and it's called from another method like this:

toString() { this. at helperMethod(); }

someone coming from Java or C# is going to expect a nonvirtual method invokation. See the Java spec for details 1. OK - everything is dynamic in JS, so they're just "confused", right? Why even invite that confusion with misleading syntax?

"private" syntax isn't a part of the current class proposal, I know. I'm just trying to think ahead - and to strike at the heart of what a "class" in JS ought to be.

kevin

# Herby Vojčík (13 years ago)

Brendan Eich wrote:

Russell Leggett wrote:

And would result in copying all of the properties from myMixin into the object literal.

Including the private-named properties? That would be bad for integrity: Alice can't give Bob an object-as-capability where the private names stay private. Bob can extract them via this hypothetical spread-in-braces and then abuse them (e.g. to brand a counterfeit object and fool Carol into thinking it was from Alice).

In any decently powerful trait composition system, you have to allow copying private-named properties (if only for class prototypes, but if it is possible for them, people will other abuse it or there should be ability ot do it in more general fashion)>

I do not want spread for objects (it is really too much, I understand, though once I also thought it is a good idea), but I think something that DRYs .{} should be there.

Let me recall one of my suggestions from before (which was tl;dr-ed becuase it was long):

  1. add 'f(x,y)' as a possibility into object-initializer (comma delimited) as well as recently-proposed max-min classes (semicolon-delimited*), for example:

    let obj = { a:2, b:4, prop("c", 6), counter() }; class Foo { constructor(x) { this.{ prop("x", x) } } counter() }

  • note: I still see max-min class basically as an object-initializer for .prototype, just with some elements banned and other mapped to slightly different syntax; so this is also just a mapping of sort.
  1. In the spirit of * for generators, allow function% definitions (and concise methods) for data-structure (having object-initializer syntax in { body } block**), to be used as delineated above:

    private x; function% prop (name, initial) { @x: initial, get [name] () { return this. at x; }, set [name] (v) { this. at x = v; } }

    private cnt; function% counter() { inc () { if (!this. at cnt) { this. at cnt = 1; } else { ++this. at cnt; } }, current () { return this. at cnt || 0; } }

** or using the mapping mentioned in * footnote, but then it must be elaborated how it is used for data-properties (and people would ask why they are not usable in classes).

Would it be (more) ok to allow such pre-built block be included in objects even if they carry private properties? I somehow think this is inevitable, sooner or later.

This does not allow to copy from any object, only from pieces that were specified as usable for composition.

/be

Herby

P.S.: Of course, if in any level of "call" a property or getter is encountered from within max-min class definition, it should throw an error, this is by no way meant as a way to sneak data-definition into classes which prohibit this in the current state.

# Herby Vojčík (13 years ago)

let obj = { a:2, b:4, prop("c", 6), counter() }; class Foo { constructor(x) { this.{ prop("x", x) } } counter() }

let obj = { a:2, b:4, prop("c", 6), counter() }; class Foo { constructor(x) { this.{ prop("x", x) } } counter(); }

semicolon missed

# Allen Wirfs-Brock (13 years ago)

On Apr 6, 2012, at 12:18 PM, Kevin Smith wrote:

As Allen mentioned, I think it would really help if you could show, say, real Java (or C# or C++) code and explain what it can do that JavaScript + private names can’t.

(Sorry for that double-post, BTW)

I can't : )

It's an abstract guarantee. Private methods (in Java let's say) give me the guarantee that I can refactor at will with them without any externally visible changes. Privately named methods in JS do not.

Look at it this way: if I have a method that looks like this:

private helperMethod() { ... }

and it's called from another method like this:

toString() { this. at helperMethod(); }

someone coming from Java or C# is going to expect a nonvirtual method invokation. See the Java spec for details [1]. OK - everything is dynamic in JS, so they're just "confused", right? Why even invite that confusion with misleading syntax?

Of course, anyone coming from a dynamically typed OO language is going to think "virtual method invocation" (except probably not using those words) because that is essentially the only kind of method invocation you get in a dynamically typed language.

But, I think I now see where you are coming form...

in a reply today to Herby I wrote (emphasis added):

Keven's original problem statement imposed the requirement that refactoring a method to use a "helper" retained the ability to individually move the method to a different object without moving any associated "private methods". Such a refactoring can only be accomplished by decomposing the method into lexically bound helper functions rather than dynamically (invocation) bound helper methods. There is nothing wrong with the sort of decomposition you are referring to. It just doesn't work for Kevin's problem statement (in any language).

In Java the combination of static typing and private visibility permits this.helper() to be lexically resolved at compilation time even though the call site syntactically looks identical to a virtual method invocation. This permits the refactoring you describe. Semantically this is exactly the same as making the helper be lexically bound function in ES.

It is primarily an idiomatic difference between programming in a static OO language and a dynamic OO language. If you do this style of lexically resolved decomposition in ES you actually can use your generic method on a different kind of object, like your original problem statement required. I don't believe that you can do that with your Java solution. It its still a type shape mismatch that is gong to have to be detected somewhere (presumably by the java.lang.reflect methods that would be used to transfer or invoke the method).

"private" syntax isn't a part of the current class proposal, I know. I'm just trying to think ahead - and to strike at the heart of what a "class" in JS ought to be.

ES is clearly a dynamically typed language rather than a statically typed language. Whatever JS classes ought to be, they need to be based upon dynamic language concepts.

# Kevin Smith (13 years ago)

It is primarily an idiomatic difference between programming in a static OO language and a dynamic OO language. If you do this style of lexically resolved decomposition in ES you actually can use your generic method on a different kind of object, like your original problem statement required. I don't believe that you can do that with your Java solution. It its still a type shape mismatch that is gong to have to be detected somewhere (presumably by the java.lang.reflect methods that would be used to transfer or invoke the method).

Right - I'm intentionally trying to do something that is not possible in Java, but in a way that a Java programmer might try to do it.

ES is clearly a dynamically typed language rather than a statically typed language. Whatever JS classes ought to be, they need to be based upon dynamic language concepts.

Definitely!

# Russell Leggett (13 years ago)

On Fri, Apr 6, 2012 at 2:58 PM, Brendan Eich <brendan at mozilla.org> wrote:

Russell Leggett wrote:

And would result in copying all of the properties from myMixin into the object literal.

Including the private-named properties? That would be bad for integrity: Alice can't give Bob an object-as-capability where the private names stay private. Bob can extract them via this hypothetical spread-in-braces and then abuse them (e.g. to brand a counterfeit object and fool Carol into thinking it was from Alice).

Yes, I thought I was following a train of thought when you said, "Is having only the literal-RHS-only special form too restrictive? It could be wrapped in an API usable from downrev script, but this means implementors of mixins must use ES6 even though consumers can use pre-ES6," but I misunderstood where you were going.

You're right about the integrity, in that use case, but preventing it across the board does certainly limit many other legitimate use cases. Could we make names more configurable? I know that there is a suggestion for a visibility flag in the private names proposal. I think this is a case somewhere in between. Access through for...in would actually allow full manipulation of the object property. Copying through the spread operator is not nearly as insecure.

Or instead of the flag being related to the name, it could just be on the property, just like enumerable, although that might not be the best semantics.

# Axel Rauschmayer (13 years ago)

I was thinking about Allen’s

class Sub extends mixin(Jane, Jack, Super) {
}

Which could have the following prototype chain.

Sub -> Jane' -> Jack' -> Super.prototype

A method in a module that works like Prototype’s Object.extend(), but copies private properties should suffice.

Variations:

  • Copy Jane and Jack into the same object.
  • Use a class declaration to define the mixins Jane and Jack. Then the prototype chain is Sub -> Jane.prototype' -> Jack.prototype' -> Super.prototype

Quoting Brendan:

Including the private-named properties? That would be bad for integrity: Alice can't give Bob an object-as-capability where the private names stay private. Bob can extract them via this hypothetical spread-in-braces and then abuse them (e.g. to brand a counterfeit object and fool Carol into thinking it was from Alice).

I don’t see how that can be avoided. One could have two kinds of private names: High-integrity ones and copy-able ones.

# Allen Wirfs-Brock (13 years ago)

On Apr 6, 2012, at 2:57 PM, Axel Rauschmayer wrote:

I was thinking about Allen’s

class Sub extends mixin(Jane, Jack, Super) {
}

Which could have the following prototype chain.

Sub -> Jane' -> Jack' -> Super.prototype

A method in a module that works like Prototype’s Object.extend(), but copies private properties should suffice.

Variations:

  • Copy Jane and Jack into the same object.
  • Use a class declaration to define the mixins Jane and Jack. Then the prototype chain is Sub -> Jane.prototype' -> Jack.prototype' -> Super.prototype

I think there is another approach that doesn't require copying. Use a proxy to define a synthetic prototype object that delegates to one or more mixin providers:

Sub -> synthesized-mixin-delegator-> Super.prototype
                       |                  |
                      V                 V
                  Jane            Jack

This is off-the-cuff so I haven't worked out the details but I think it almost works. I think it will work for string-keyed properties. Unfortunately, as currently specified it won't work for private name properties because the proxy traps that would need to do the delegated sideways property lookups are not passed the actual private name object. Instead they are passed a surrogate that can be matched against a known private name but which can't be used for a direct property access. There might be ways to fix that which still don't expose the actual private name to misuse. For example, perhaps a private name surrogate could expose a method that does a property lookup using actual private name without exposing the actual name. The security focused guys would probably have to tell us whether they could live with that additional exposure.

Quoting Brendan:

Including the private-named properties? That would be bad for integrity: Alice can't give Bob an object-as-capability where the private names stay private. Bob can extract them via this hypothetical spread-in-braces and then abuse them (e.g. to brand a counterfeit object and fool Carol into thinking it was from Alice).

This has always been my issue with the integrity constraints that have been specified for private names. They really get in the way of the meta-programming. You can have a hight integrity or you can have rich meta-programming. It isn't clear to me that you can have both at the same time.

I don’t see how that can be avoided. One could have two kinds of private names: High-integrity ones and copy-able ones.

That was the rationale behind harmony:private_name_objects#possible_visibility_flag . This is something that I pushed quite hard for a while but it didn't seem to get a lot of traction. One reason, that I have kind of given up on it is that I don't think many programmer would actually code something like:

const methodName = name.create("printName",true);

to define their meta programmable private names.

However, if we support private foo; //pretty much means: const foo=name.create("foo")

then maybe it wouldn't be such a leap to also support: protected bar; //pretty much means: const bar-name.create("bar",true)

In other words, protected gives you a private name that doesn't have the integrity constraints. This is actually not an unreasonable understanding of "protected" in the contest of a meta-programable dynamic object system.

# David Bruant (13 years ago)

Le 07/04/2012 00:57, Allen Wirfs-Brock a écrit :

On Apr 6, 2012, at 2:57 PM, Axel Rauschmayer wrote:

I was thinking about Allen's

class Sub extends mixin(Jane, Jack, Super) {
}

Which could have the following prototype chain.

Sub -> Jane' -> Jack' -> Super.prototype

A method in a module that works like Prototype's Object.extend(), but copies private properties should suffice.

Variations:

  • Copy Jane and Jack into the same object.
  • Use a class declaration to define the mixins Jane and Jack. Then the prototype chain is Sub -> Jane.prototype' -> Jack.prototype' -> Super.prototype

I think there is another approach that doesn't require copying. Use a proxy to define a synthetic prototype object that delegates to one or more mixin providers:

Sub -> synthesized-mixin-delegator-> Super.prototype
                       |                  |
                      V                 V
                  Jane            Jack

This is off-the-cuff so I haven't worked out the details but I think it /almost/ works. I think it /will/ work for string-keyed properties. Unfortunately, as currently specified it won't work for private name properties because the proxy traps that would need to do the delegated sideways property lookups are not passed the actual private name object. Instead they are passed a surrogate that can be matched against a known private name but which can't be used for a direct property access. There might be ways to fix that which still don't expose the actual private name to misuse. For example, perhaps a private name surrogate could expose a method that does a property lookup using actual private name without exposing the actual name. The security focused guys would probably have to tell us whether they could live with that additional exposure.

Or maybe revive tha alternate private name proposal esdiscuss/2011-December/018997 (which is nothing less than the visibility flag wit adapted proxy semantics) ?

# Axel Rauschmayer (13 years ago)

On Apr 6, 2012, at 2:57 PM, Axel Rauschmayer wrote:

I was thinking about Allen’s

class Sub extends mixin(Jane, Jack, Super) {
}

Which could have the following prototype chain.

Sub.prototype -> Jane' -> Jack' -> Super.prototype

A method in a module that works like Prototype’s Object.extend(), but copies private properties should suffice.

Variations:

  • Copy Jane and Jack into the same object.
  • Use a class declaration to define the mixins Jane and Jack. Then the prototype chain is Sub.prototype -> Jane.prototype' -> Jack.prototype' -> Super.prototype

I think there is another approach that doesn't require copying. Use a proxy to define a synthetic prototype object that delegates to one or more mixin providers:

Sub.prototype -> synthesized-mixin-delegator-> Super.prototype
                       |                  |
                      V                 V
                  Jane            Jack

This is off-the-cuff so I haven't worked out the details but I think it almost works. I think it will work for string-keyed properties. Unfortunately, as currently specified it won't work for private name properties because the proxy traps that would need to do the delegated sideways property lookups are not passed the actual private name object. Instead they are passed a surrogate that can be matched against a known private name but which can't be used for a direct property access. There might be ways to fix that which still don't expose the actual private name to misuse. For example, perhaps a private name surrogate could expose a method that does a property lookup using actual private name without exposing the actual name. The security focused guys would probably have to tell us whether they could live with that additional exposure.

[Quick aside: I forgot to write Sub.prototype, added above; object exemplars make this less error-prone]

With either approach, I wonder what the performance implications are. How does one let the engine know, statically, what the prototype chain will be?

Quoting Brendan:

Including the private-named properties? That would be bad for integrity: Alice can't give Bob an object-as-capability where the private names stay private. Bob can extract them via this hypothetical spread-in-braces and then abuse them (e.g. to brand a counterfeit object and fool Carol into thinking it was from Alice).

This has always been my issue with the integrity constraints that have been specified for private names. They really get in the way of the meta-programming. You can have a hight integrity or you can have rich meta-programming. It isn't clear to me that you can have both at the same time.

I don’t see how that can be avoided. One could have two kinds of private names: High-integrity ones and copy-able ones.

That was the rationale behind harmony:private_name_objects#possible_visibility_flag . This is something that I pushed quite hard for a while but it didn't seem to get a lot of traction. One reason, that I have kind of given up on it is that I don't think many programmer would actually code something like:

const methodName = name.create("printName",true);

to define their meta programmable private names.

However, if we support private foo; //pretty much means: const foo=name.create("foo")

then maybe it wouldn't be such a leap to also support: protected bar; //pretty much means: const bar-name.create("bar",true)

In other words, protected gives you a private name that doesn't have the integrity constraints. This is actually not an unreasonable understanding of "protected" in the contest of a meta-programable dynamic object system.

How often does one write high-integrity objects? I suspect that only specialists would want to use name.create("printName",true). Everyone else uses private. (Right?)

# Allen Wirfs-Brock (13 years ago)

On Apr 6, 2012, at 4:11 PM, Axel Rauschmayer wrote:

... I think there is another approach that doesn't require copying. Use a proxy to define a synthetic prototype object that delegates to one or more mixin providers:

Sub.prototype -> synthesized-mixin-delegator-> Super.prototype
                                     |                  |
                                    V                 V
                                Jane            Jack

This is off-the-cuff so I haven't worked out the details but I think it almost works. I think it will work for string-keyed properties. Unfortunately, as currently specified it won't work for private name properties because the proxy traps that would need to do the delegated sideways property lookups are not passed the actual private name object. Instead they are passed a surrogate that can be matched against a known private name but which can't be used for a direct property access. There might be ways to fix that which still don't expose the actual private name to misuse. For example, perhaps a private name surrogate could expose a method that does a property lookup using actual private name without exposing the actual name. The security focused guys would probably have to tell us whether they could live with that additional exposure.

[Quick aside: I forgot to write Sub.prototype, added above; object exemplars make this less error-prone]

With either approach, I wonder what the performance implications are. How does one let the engine know, statically, what the prototype chain will be?

Proxy delegated property lookups can still be cached, as long as there is a reasonable way to invalidate the caches when the delegated property mapping changes. Modern engines presumably depend upon the (relatively) immutable nature of prototype chains and some sort of internal notification when the properties of a cached prototype object are modified. My scheme gives ES code in proxies control of the property lookup process so it would probably be necessarily to provide a mechanism that could be used by a lookup delegator object to announce its property mapping has changed and hence that any engine-level caches should be invalidated. There would definite design work requried to enable this, but it is something I have seem work in implementations of other languages.

Quoting Brendan:

Including the private-named properties? That would be bad for integrity: Alice can't give Bob an object-as-capability where the private names stay private. Bob can extract them via this hypothetical spread-in-braces and then abuse them (e.g. to brand a counterfeit object and fool Carol into thinking it was from Alice).

This has always been my issue with the integrity constraints that have been specified for private names. They really get in the way of the meta-programming. You can have a hight integrity or you can have rich meta-programming. It isn't clear to me that you can have both at the same time.

I don’t see how that can be avoided. One could have two kinds of private names: High-integrity ones and copy-able ones.

That was the rationale behind harmony:private_name_objects#possible_visibility_flag . This is something that I pushed quite hard for a while but it didn't seem to get a lot of traction. One reason, that I have kind of given up on it is that I don't think many programmer would actually code something like:

const methodName = name.create("printName",true);

to define their meta programmable private names.

However, if we support private foo; //pretty much means: const foo=name.create("foo")

then maybe it wouldn't be such a leap to also support: protected bar; //pretty much means: const bar-name.create("bar",true)

In other words, protected gives you a private name that doesn't have the integrity constraints. This is actually not an unreasonable understanding of "protected" in the context of a meta-programable dynamic object system.

How often does one write high-integrity objects? I suspect that only specialists would want to use name.create("printName",true). Everyone else uses private. (Right?)

As I described it above, I would expect anyone doing classic inheritance based implementation reuse or use any meta-programming based frameworks would want to use 'protected'. Anyone who was primarily concerned about high-integrity would want to use 'private'