Private properties: avoiding reification of names, unit-testing

# Axel Rauschmayer (14 years ago)

harmony:private_name_objects

I would like to avoid reifying the names of private properties (as variables holding name objects). Wouldn’t it be enough if they were private relative to the current class/constructor? Then the following would work (strawman syntax):

function MyClass(pwd) { this[private(MyClass, "pwd")] = pwd; } MyClass.prototype.isPwd = function(pwd) { return this[private(MyClass, "pwd")] === pwd; }

Another consideration: private methods are sometimes helpers that have to be unit-tested. Thus, one should be able to access them “from outside”. That’s something you can’t do if you use a closure for private data.

# Brendan Eich (14 years ago)

On Oct 2, 2011, at 4:43 PM, Axel Rauschmayer wrote:

harmony:private_name_objects

I would like to avoid reifying the names of private properties (as variables holding name objects). Wouldn’t it be enough if they were private relative to the current class/constructor? Then the following would work (strawman syntax):

function MyClass(pwd) { this[private(MyClass, "pwd")] = pwd;

You just reified, in the square brackets.

Worse, you seem to make it possible for anyone with access to MyClass to pass a string into this private pseudo-function (I hope it cannot be passed around as a funarg!) and get out the reified private name!

If you mean only for the private(C,id) form to be valid in [], then surely we should be talking about shorter and less special and wrongly general-looking syntax, e.g. infix @.

} MyClass.prototype.isPwd = function(pwd) { return this[private(MyClass, "pwd")] === pwd; }

With the current proposal, you can do something like this:

{ import create from "@name"; const pwdName = create("pwd"); function MyClass(pwd) { this[pwdName] = pwd; } MyClass.prototype.isPwd = function(pwd) { return this[pwdName] === pwd; } }

It might be too verbose to have to import create from "@name" and call it. We can address that with sugar or shorter API design. But that's not what you were addressing.

# Axel Rauschmayer (14 years ago)

this[private(MyClass, "pwd")] = pwd;

You just reified, in the square brackets.

True, but I didn’t introduce a variable with a scope (see below for more detailed arguments).

Worse, you seem to make it possible for anyone with access to MyClass to pass a string into this private pseudo-function (I hope it cannot be passed around as a funarg!) and get out the reified private name!

If you mean only for the private(C,id) form to be valid in [], then surely we should be talking about shorter and less special and wrongly general-looking syntax, e.g. infix @.

Absolutely, the syntax was 100% strawman. I’d expect it to be highly optimized behind the scenes (to be treated like a literal by a compiler) and to be replaced by syntax that looks much better, e.g.: this.MyClass at pwd = pwd;

With the current proposal, you can do something like this:

{ import create from "@name"; const pwdName = create("pwd"); function MyClass(pwd) { this[pwdName] = pwd; } MyClass.prototype.isPwd = function(pwd) { return this[pwdName] === pwd; } }

It might be too verbose to have to import create from "@name" and call it. We can address that with sugar or shorter API design. But that's not what you were addressing.

Yes, as it stands, the proposal is quite usable. You can even export pwdName to unit tests (which would make more sense if it was a method identifier). I only wish we could avoid redundantly repeating private names, usually far away from where they are actually used. I like that this approach can be retrofitted so easily, but suspect that having to “declare” each private name before using it will get tired quickly.

# Brendan Eich (14 years ago)

On Oct 2, 2011, at 5:16 PM, Axel Rauschmayer wrote:

Yes, as it stands, the proposal is quite usable. You can even export pwdName to unit tests (which would make more sense if it was a method identifier). I only wish we could avoid redundantly repeating private names, usually far away from where they are actually used. I like that this approach can be retrofitted so easily, but suspect that having to “declare” each private name before using it will get tired quickly.

Could be. My example closed the block in which the private name was scoped without exporting MyClass! Oops. If I'd used a module with inline body, then export prefixing the function MyClass would do the job, but it's a bit heavy.

Still, we moved private name objects ahead, and rightly so, without adding syntax for them. Syntax is hard (I underestimate this sometimes).

# Axel Rauschmayer (14 years ago)

Still, we moved private name objects ahead, and rightly so, without adding syntax for them.

That makes sense, as it doesn’t preclude sugar in the future. One thing to consider: Do private names need to be globally unique or is unique-per-class enough? The former enables many intriguing other applications, but it might preclude moving to a nicer syntax later on.

I used to think I’d never need private properties and would just prefix "_" to internal names. But then @juandopazo pointed out that this does not protect you against name clashes – a big issue once we have traits, but already relevant with subclassing.

Syntax is hard (I underestimate this sometimes).

Keeping all of the syntax stuff straight in one’s mind seems to me like studying law. Many ideas that seem great at first sight turn out to be impractical, due to some pesky old law from 1890. Alas, there is no other way to do this.

# Brendan Eich (14 years ago)

On Oct 2, 2011, at 5:54 PM, Axel Rauschmayer wrote:

Still, we moved private name objects ahead, and rightly so, without adding syntax for them.

That makes sense, as it doesn’t preclude sugar in the future. One thing to consider: Do private names need to be globally unique or is unique-per-class enough? The former enables many intriguing other applications, but it might preclude moving to a nicer syntax later on.

They are objects with unique identity. Objects have identity, primitive types do not. You can forge a string. You can't forge an object. It's a capability.

If you mean to suggest two classes C and D, each with a private name P, where C's P === D's P, that is a very bad idea. It's a capability leak, a channel for communicating, attacking, and subverting D from C.

# Axel Rauschmayer (14 years ago)

Yeah, sorry half-cocked idea, badly described: Generate a unique private property name as a pair (Class ID, property name).

If you rely on object IDs, you don’t have to think about that at all, only if you want to reproduce a private name somewhere else (say, in a unit test) without passing the name object around.

# Rick Waldron (14 years ago)

I'm curious to know why unit-testing is the motivation. In most cases, encapsulation should never be broken for sake of exercising a public api. Private data and methods should do their work while the public api bears the results of the complete logic set, ie. the public api should fail its tests if the private data and methods are not behaving correctly.

Rick

And Dave Thomas and Andy Hunt, in their book Pragmatic Unit Testing [6www.artima.com/suiterunner/private3.html#r6],

write:

In general, you don't want to break any encapsulation for the sake of testing (or as Mom used to say, "don't expose your privates!"). Most of the time, you should be able to test a class by exercising its public methods. If there is significant functionality that is hidden behind private or protected access, that might be a warning sign that there's another class in there struggling to get out.

# Rick Waldron (14 years ago)

Whoops! Forgot to cite that source... www.artima.com/suiterunner/private2.html

# Axel Rauschmayer (14 years ago)

I'm curious to know why unit-testing is the motivation. In most cases, encapsulation should never be broken for sake of exercising a public api. Private data and methods should do their work while the public api bears the results of the complete logic set, ie. the public api should fail its tests if the private data and methods are not behaving correctly.

Ideally, yes. However, I occasionally write private helpers that I would like to test.

# Dean Landolt (14 years ago)

On Mon, Oct 3, 2011 at 7:16 AM, Axel Rauschmayer <axel at rauschma.de> wrote:

I'm curious to know why unit-testing is the motivation. In most cases, encapsulation should never be broken for sake of exercising a public api. Private data and methods should do their work while the public api bears the results of the complete logic set, ie. the public api should fail its tests if the private data and methods are not behaving correctly.

Ideally, yes. However, I occasionally write private helpers that I would like to test.

Exactly. It's a matter of domains -- it may be easier to prove sound the constituent components of a composition than to exercise the composition's full range. This is completely orthogonal to issues of encapsulation.