using Private name objects for declarative property definition.

Brendan Eich brendan at mozilla.com
Mon Jul 11 16:18:53 PDT 2011


On Jul 11, 2011, at 12:40 PM, Allen Wirfs-Brock wrote:

> isGenerator is essentially a value test rather than class-like categorization.  Methods work well for this because a method can dynamically inspect the value being tested in order to make the determination.

I'm not so sure about this now. I was just reviewing with Dave how the design evolved. We had Function.isGenerator, analogous to Array.isArray. For taskjs, Dave had thought he had a use-case where the code has a function and wants to know whether it's a generator. It turned out (IIUC) that what he really wanted (this will warm your heart) was simply the duck-type "does it implement the iterator protocol" test.

On the other hand, code that wants to ask "is this *value* a generator?" may not have a-priori knowledge that the value is a function, so a class method such as Function.isGenerator wins.

Indeed Array.isArray, apart from being named as if it were a class method just to avoid polluting the global (or some other) object, is a predicate function -- not  a method. You can't know that you have an Array instance, never mind an object-type (typeof sense) value, all the time. If you don't know whether x is an array or even an object as opposed to a primitive, just call Array.isArray(x).

This strongly suggests to me that we want predicate functions. For the "@name" built-in module, isName is one such. There's no need to stick that into a "class object" just to avoid name pollution, since the module lets the importer name the import.

To rehash a subtle point again: our Function.prototype.isGenerator method is particular nasty if a user knows Array.isArray and expects Function.isGenerator(x) to test whether x is a generator (or any non-generator, whether function or not) value. That expression actually evaluates equivalently to Function.prototype.isGenerator.call(Function, x), and x is ignored -- and Function is of course not a generator.

So I think we took the wrong door here. Function.isGenerator by analogy to Array.isGenerator, or an isGenerator export from "@iter" (equivalent semantically), is the best way.


> However, methods are less desirable for class-like categorization because they require an existence predicated call (f.isFoo && f.isFoo()) which potentially leads to monkey patching Object.prototype (Object.prototype.isFoo = function(){return false}).  A truthy data property is a plausable alternative that avoids the need for monkey patching, but it doesn't work for value tests.

Only if you know you have an object. If the predicate you need has signature "any -> boolean" then you want a function.


> If a value tests can be recast as a class-like categorization then the data property approach works for it.

Right, but you can't "recast" for any type of value without writing a prior typeof conjunct. Or did you mean something else by "recast"?


> Using an alternative prototype for all values in a "subclass" (eg all generators) seems like a technique that might be plausible in situations like this.  It is essentially just a way to factor out of each generator the storage of the true value for the isGenerator property.  It doesn't require the exposure of a separate Generator constructor. 

I think this misses the mark. Both Array.isArray in ES5 and Function.isGenerator in ES.next are testing nominal type tag. They are not testing some ad-hoc boolean "tag" that is not reliable and that can be forged.


> We are trying to generalize to a pattern to apply to all (or at least most isFoo) situations.  Here is what we seem to have observed so far:
> 
> A isFoo method works well for value classification for situations where you will generally  already know the "class" of the value.

Agreed.


> A independent classification function (perhaps hung from a constructor) may be a good solution when value classification will generally be done in situations where the "class" of the value is not predetermined.

Agreed.


> A truthy isFoo data property works will for class-like categorization as long as all values that share the same prototype are considered members of the same category.

Disagree for the use-cases you applied this to. ES5 mandates a [[Class]] check for Array.isArray, and the impetus there was cross-frame Array classification based on nominal really-truly-built-in-Array type tag.

Ditto for any isGenerator worth its name, that is, to the extent that the use-case for isGenerator does not simply want to test "is it an iterator factory", which could be a structural or duck-type test.

/be


More information about the es-discuss mailing list