using Private name objects for declarative property definition.

Allen Wirfs-Brock allen at wirfs-brock.com
Sat Jul 9 19:22:18 PDT 2011


On Jul 9, 2011, at 5:02 PM, Brendan Eich wrote:

> On Jul 9, 2011, at 4:32 PM, Allen Wirfs-Brock wrote:
> 
>> On Jul 9, 2011, at 1:43 PM, Brendan Eich wrote:
> 
>> ... However, if that isn't your concern (and my perspective is that in most cases it shouldn't be) you might as well use a public trademark property on instances in which case a direct test for that property is probably all you need.
>> 
>> However, I agree that best practice would be for such property to have no access side-effects, regardless of how they were are implemented.
> 
> I'm not sure which however wins. ;-)
> 
> We have Array.isArray, Proxy.isTrapping (soon to be Proxy.isProxy?), Function.prototype.isGenerator (could be Function.isGenerator -- note different signature, so don't want both -- Object.getPrototypeOf(Function) === Function.prototype). I think methods on built-ins are winning.

I think instances that are self identifying or self classify are best and should be the default approach. Whether via a method or data properties is a secondary issue. However, some times you need to use have a separate object (let's call it an oracle) that is responsibility for doing the identification/classification.  There are various reasons for using an oracle:
1) stratification -  Proxy.isProxy is an example
2) It is impossible or inconvenient to add the classification interface to the appropriate instance interface.  I think that was the reason for Array.isArray.  We were more afraid of adding it to Array.prototype than to Array.
3)  Externally imposed classification that is not cohesive with the rest of an object's behavior. For example, isSerializable is probably dependent upon a specific kind of serializer.  Their might even be multiple different kinds of serializers installed in a system.

> 
> 
>> In general yes.  Let's say a new kind of Crazy DOM node interfaces defines that it has a isCrazyNode property and also has semantics that requires the use of a Proxy to natively implement it in JS. In that case isCrazyNode needs to be implemented via the Proxy.
>> 
>> On the other hand, we don't want random objects to start exposing isProxy properties just because a Proxy was used to implement it.  That would be unnecessarily exposing implementation details.  I fall back o my standard test, can Proxies be used to implement ES Array instances.  If such instances implemented via Proxies had a visible isProxy property they would not be a accurate implemented on the ES spec. for array instances.
> 
> Right, so Proxy.isProxy -- on the built-in namespace object (like a standard module binding), not on each proxy instance.
> 
> But if you want isCrazyNode intercession via a Proxy, as you state above. For an Array emulation via Proxy, you'd have to monkey-patch Array.isArray.

Well, if you were actually using Proxy to implement Array, you would probably also be providing the Array implementation.

However, arguably Array.isArray really should have been Array.prototype.isArray.  We treated as a case 2 from above.  May we really didn't need to, but that's water over dam.  I don't think we should use it as precedent for more greenfield situations.

> 
> 
>> My argument for self-identify objects rather than independent predicates is above.
> 
> A brand or trademark is quite different from some old truthy data property. One could use private name objects for branding, no forgeries possible.

Yes, but I think the situation where you really need a non-forgage  brand are relative rare compared to the the need to do more basic classificaiton.

> 
> 
>> If you buy that self-identify is better, then the question becomes data property or instance methods. To me that comes down to, if someone is going to have to code  if (typeof obj.isFoo == 'function' && obj.isFoo()) ... why not just say if (obj.isFoo)...
> 
> I agree with that style when detecting a method.
> 
> BTW, it leads to the obj.isFoo?() conditional call idea (along with ?.). We never reached consensus there.

But f the method implementation is simply going to be return true or return false why do yo need to call it at all?

> 
> 
>> A major source of monkey patch in large Smalltalk applications was people adding isFoo ^false  methods to  Object so they could test arbitrary  objects for Fooness. I'm assume that as people start building complex JS applications using rich inheritance based duck typed "class" libraries they are going to find the need to do the same sort of classification testing.
> 
> This was instance-side, right? Seems like it would work with class-side inheritance too.

In Smalltalk, yes, on the instance side. But you can always reach the class side from the instance.  Essentially the equivalent of JS obj.constructor.

Smalltalk coe guidelines would general say (but I'll use JS syntax here instead of Smalltalk):


obj.isFoo    //ok
obj.constructor.isFoo(obj)  //less ok, unless there is a good reason
Foo.isFoo(obj)   //even less good

In the first two you are only statically coupled to obj
in the third you are statically coupled to both obj and Foo.

> 
> 
>> Data property testing is a way to accomplish this without the monkey patching.  
> 
> You mean without all the stub ^false / return false methods polluting Object? That is true, but again I return to the precedent (however recent) we've set: Array.isArray. Class-side, not instance-side.

right.  And I think that Array.isArray was a special case exception rather than an exemplar for the future.

Allen


More information about the es-discuss mailing list