using Private name objects for declarative property definition.

Allen Wirfs-Brock allen at wirfs-brock.com
Sat Jul 9 16:32:06 PDT 2011


On Jul 9, 2011, at 1:43 PM, Brendan Eich wrote:

> On Jul 9, 2011, at 11:19 AM, Allen Wirfs-Brock wrote:
> 
>> A consideration here is that whatever conventions we follow sets a precedent for user written code. I don't think we want to encourage the addition of such classification functions to Object or Object.prototype.  So from that perspective Array.isArray, Function.isGenerator, and Proxy.isProxy are better exemplars than Object.isArray, etc.
> 
> Do we want users extending Array or Function? Same problem as applies to object. In Edition N+1 we then collide. I don't see a difference in kind here.


No, but the example we are setting is that if you are hanging such predicates off of constructors they should be constructors related to what you are testing rather than Object.


> 
> 
>> Another consider is whether such classification schemes should use methods or data properties. An instance side, isFoo() method to be useful generally will also require the addition of an isFoo() method to Object.prototype while a instance side-data property can be tested without augmenting Object.prototype (({}.isFoo) is a falsey value).  Also most user code classification schemes will require some sort of instance-side tagging or testing to actually determine whether the instance is in the category. 
>> 
>> For those reasons, I think the best classification scheme is usually via an instance-side public data property with a truthy value. 
> 
> I don't agree, because detecting truthy valued properties from host objects (proxies) can have effects. Calling a well-known predicate function that has no side effects is better.

That may be true for host objects/proxies.  But in general for plain user defined objects such a predicate function is going to have to inspect the instance for some identifying characteristic  (assuming it is a situation where instance of isn't adequate or desirable).  That characteristic is presumably going to manifest some sort of trademark property on the instance.  If you are considered about spoofing I can see why you might use a well known predicate that depends upon a private name based trade mark.  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.

> 
> 
>> But there are some exceptions.  For the legacy built-ins such as Array is less risky from compatibility perspective to add properties to the constructor than to the prototype. However, for new built-in this isn't an issue. Another consider is meta layering: meta-layer functions shouldn't be exposed on application layer objects. Proxy.isProxy is a good example.  Proxy instances operate as application objects in the application layer.  The public face of application objects shouldn't be polluted with a meta-layer method such as isProxy.
> 
> Do we want intercession by proxies for any of these isFoo predicates?

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.

> 
> 
>>> Why not Function.isGenerator? Because the prototype method is more usable when you have a function in hand and you want to know whether it's a generator. If you have any value x, you'll need (typeof x === 'object' && 'isGenerator' in x) guarding the x.isGenerator() call, but we judged that as the less common use-case.
>> 
>> Why not just make it a boolean-valued data property?
> 
> See above, and also isArray and other isFoo predicates (isNaN) realized via functions, not data properties.

OK, if you have a distinct function that isn't a method instance.  However, in general you have to hang that function off of come object (such as the constructor).  That means that a test site must have access (probably lexical) to the container of the predicate and must know the name of the predicate.  The contain is probably accessed via a module import and if it is a constructor you get access to capabilities (such as creating instances) that go far beyond simply category testing.  That violates the principle of least privilege.

I think self identifying objects have much better modularity.  All you need to know is the name of the categorization property and that should be part of the  instance interface that you are already depending upon.  If you do it that way there is no need to import a constructor or other container of the predicate and expose additional capabilities that might be available from those.  BTW, my concern here isn't security based.  It is that  minimizing module dependencies give you more maintainable code.


> 
> 
>>> Could be we were wrong, or that being consistent even in the hobgoblin/foolish sense is better. But Object.isGenerator crowds poor Object more.
>>> 
>>> Notice how there's no need for Function.isFunction or Object.isFunction, due to typeof (function(){}) === 'function'. That's another dawn-of-time distinction that does not fit the primitiive types vs. object classification use-case I mentioned in the last message -- testing whether x is callable via typeof x === 'function', at least for native if not host objects, is the use-case here.
>> 
>> But this is a special case that does not extrapolate to other situations.
> 
> Agreed, although we've said we'll consider typeof-result extensions in the future, e.g., for user-defined value types.
> 
> 
>>> Some of these questions are at this point more about aesthetics and Principles than about usability. And we have historic messiness in how things have grown.
>> In a utilitarian language like JS, usability and aesthetic considerations should be closely linked and consistency is a significant usability factor. .  Principles are a way to get consistent application of such  aesthetics . 
> 
> It's hard to disagree, but that doesn't settle disagreements, e.g., on data properties that are true-valued or missing, so undefined-falsy, vs. pure predicate functions.

My argument for self-identify objects rather than independent predicates is above.

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)...

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.  Data property testing is a way to accomplish this without the monkey patching.  

Allen



More information about the es-discuss mailing list