Re: delegating to typed objects

2007-07-09 Thread Brendan Eich
On Jul 9, 2007, at 4:51 PM, Kris Zyp wrote:

 You are right, it doesn't work for a Date, but it does work on an  
 Array.

Only if you don't call toString or toLocaleString.

Most Array and String prototype methods are intentionally generic.  
Too bad we didn't generalize this all over the place in 1996 (sorry,  
Tucker). To call such methods on other types of objects, in ES1-3 you  
have to use .apply or .call -- in ES4 there are Array.slice, .e.g.,  
counterparts that take a leading explicit this-object parameter for  
Array.prototype.slice.

 The incompatibility of calling setTime on the Date instance seems  
 to have more to do with the natural inclination to make primitives  
 classes final (I am not sure if they are in ES4, but in Java they  
 are final) than class method binding. While I am still hoping for  
 option 1, I will say that throwing an (incompatible object) error  
 on f.m() seems more consistent than the other possibilites that I  
 have been countering: non-shallow assignments, automatic this- 
 binding, or preventing the assignment of prototype objects that  
 happen to be instances of user defined classes.

Yeah.

/be


___
Es4-discuss mailing list
Es4-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es4-discuss


Re: delegating to typed objects

2007-07-09 Thread Brendan Eich
On Jul 9, 2007, at 9:09 PM, Kris Zyp wrote:

 I think it seems worth considering what a programmer is intending  
 if writes
 a function and defines a prototype for it. This is an explicit  
 action to
 create objects that inherit from the prototype object. If a user has
 specifically written code to inherit from c, wouldn't he expect to  
 inherit
 everything from c, including fixtures and methods (that work, not  
 throw
 incompatibility errors)?

Because it's possible to violate the type constraints on fixtures in  
the non-subclass object created by the function constructor?

That seems like a real possibility. The class methods, unless  
qualified by prototype, really do constrain the type of |this|, and  
that's a big part of the value of classes. Consider the alternatives:

1. Classes do not constrain types, have to check everything that  
might be different in a delegating object method call (|this|,  
members) at runtime.

2. Delegate creation via (new F) given F.prototype = new C and class  
C must make an instance of C, not of Object. This is not backward  
compatible.

3. Delegating to class methods from an instance created via (new F)  
somehow (how?) wraps the class methods with type-constraining  
wrappers, so the cost is not born by the class methods when called  
class instances.

I don't think any of these is a good plan.

 If we access fixture properties and methods just as
 we do do expando properties and functions, why wouldn't we expect
 programmers to want and fixtures and methods to be inherited?

Because fixture means fixed, unlike expando or ad-hoc properties. If  
you want to delegate without type constraints, you have the tools in  
JS already. Using classes means either locking down fields and  
methods, or (if you use dynamic and prototype qualifiers) emulating  
the ES1-3 builtins and stuff like the DOM classes. In the former  
case, you should get a type error. In the latter, you can do what you  
say you want, but you have to write the class with extra qualifiers.

 Defining
 prototypes is not something that just automatically happens in  
 places, but
 is a very intentional definition on the part of a programmer in  
 which they
 want and expect inheritance.

So is defining a class.

So is mixing the two definitions, constructor function prototype (not  
a definition but an assignment expression, of course) and class  
definition.

 And I still appeal to the symmetry and consistency of the is  
 operator
 operating exactly the same as the old instanceof operator when the  
 testing
 against a class:
 (a is C) == C.prototype is in a's delegate chain (at least when  
 __proto__
 has not been externally modified) that would be true with option 1.

The is operator tests whether its left operand is an instance of a  
subtype (:, which is reflexive, so includes the type itself) of its  
right operand. It is not instanceof, which tests whether its left  
operand has a prototype equal to the value of the 'prototype'  
property of its right operand (if that operand is an object). Don't  
mention mutable __proto__ (it's a botch and irrelevant). Just consider:

function B() {}
function D() {}
D.prototype = new B
d1 = new D
assert(d1 is B)  // false
assert(d1 instanceof B)  // true
D.prototype = new Object
d2 = new D
assert(d2 is B)  // false
assert(d2 instanceof B)  // false

class B {}
class D extends B {}
d1 = new D
assert(d1 is B)  // true
assert(d1 instanceof B)  // true
D.prototype = new Object // silently fails because 'prototype'
  // is DontDelete and ReadOnly in D
d2 = new D
assert(d2 is B)  // still true
assert(d2 instanceof B)  // still true

Not only can't you modify D.prototype, but (d1 is B) is true for  
class B but false for function B.

You are right that is and instanceof agree in all four assertions for  
classes. But only one of four corresponding assertions for  
constructor functions is true. Classes are not functions, not by a  
long shot.

/be
___
Es4-discuss mailing list
Es4-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es4-discuss