It appears (at least in the reference implementation) that one there is an
object that has a delegate object that is a typed object, that overwriting
members in the top instance is sometimes not possible. For example:
class C { var x : String; }
c = new C;
function F() {}
F.prototype=c;
f = new F; // f.proto = c
f.x =4;
If you do this c.x now equals "4", that is the value is not set in f, but it
goes up the prototype chain and sets 4 into x (and does implicit conversion).
This behavior is incorrect. For compatibility with ES3, property writes must
be shallow.
I think I realize why this is being done. c is of type C, but f is not of
type C, but it still must maintain consistency in the typing of its members
(so instance method can be assured of the right types, I presume). However,
this seems that like quite unintuitive behavior for JavaScript. Generally one
would expect the statement f.x=4; to only affect f, not f's delegate (c).
Agreed.
Was it ever considered to enforce a system where if f delegates to c, that f
must be the same type (or subtype) as c? This could be done by allowing
[[Class]] definition to be inheritable from a delegate (in this f would not
define what [[Class]] it is, but would inherit it's class definition from c
which defines it's class to be C), and maintaining prototype chain
consistency with class inheritance.
This seems a little too magic for me, and this is what classes are for. A
delegate (i.e. proto) is just a delegate whose static type is always
Object. If you want to benefit of type checking on instance properties, you
need to use classes, interfaces and/or structural object types to constrain
the instance.
If you want x to be a delegated and override-able "plain old" property, not a
fixture, declare C thus:
class C { prototype var x : String; }
To be clear, in the example above this would put a expando property x¹ on
f.proto.__proto__¹.
Without prototype qualifying var, you get a fixture, and fixtures are always
fixed as to meaning and type constraint by type of their containing class.
That's their raison d'être.
Having said that, I'll admit that your suggested change to the class
instantiated by (new F) given F.prototype = new C is interesting and
provocative. By default, ES4 as reference-implemented follows ES3 and makes
(new F) for all functions F creates a new Object instance. But native
constructor functions and classes can make instances of narrower types than
Object, obviously (Date, RegExp, etc.). And some built-in classes (at least
RegExp per ES3's spec, although no browsers follow this) have prototype
properties of type Object.
So there's an attractive symmetry in making F.prototype = new C cause (new F)
to instantiate C instances.
If we did this, you would still have fixtures overriding prototype properties,
but you would have a fixture per (new F) instance, not one in the (new C)
prototype instance as in your example (the one denoted by the variable |c|).
That would avoid the pigeon-hole problem. (You could also use the prototype
qualifier as in my counter-example.)
If we did this, you might also (or might not) want |dynamic| in front of class
C {...} in order to allow "expandos".
Still thinking, comments welcome.
See my comment before my last one. Making prototypes more like classes would
make them less flexible, more complicated, and duplicate functionality that
is already available. I don¹t see the point, but maybe I¹m missing the use
case.
On 7/2/07 1:29 PM, Brendan Eich wrote:
> On Jul 2, 2007, at 9:21 AM, Kris Zyp wrote:
>
>> It appears (at least in the reference implementation) that one there is an
>> object that has a delegate object that is a typed object, that overwriting
>> members in the top instance is sometimes not possible. For example:
>>
>> class C { var x : String; }
>>
>> c = new C;
>>
>> function F() {}
>>
>> F.prototype=c;
>>
>> f = new F; // f.__proto__ = c
>>
>> f.x =4;
>>
>> If you do this c.x now equals "4", that is the value is not set in f, but it
>> goes up the prototype chain and sets 4 into x (and does implicit conversion).
>>
This behavior is incorrect. For compatibility with ES3, property writes must
be shallow.
>>
>> I think I realize why this is being done. c is of type C, but f is not of
>> type C, but it still must maintain consistency in the typing of its members
>> (so instance method can be assured of the right types, I presume). However,
>> this seems that like quite unintuitive behavior for JavaScript. Generally one
>> would expect the statement f.x=4; to only affect f, not f's delegate (c).
>>
Agreed.
>>
>> Was it ever considered to enforce a system where if f delegates to c, that f
>> must be the same type (or subtype) as c? This could be done by allowing
>> [[Class]] definition to be inheritable from a delegate (in this f would not
>> define what [[Class]] it is, but would inherit it's class definition from c
>> which defines it's class to be C), and maintaining prototype chain
>> consistency with class inheritance.
>>
This seems a little too magic for me, and this is what classes are for. A
delegate (i.e. __proto__) is just a delegate whose static type is always
Object. If you want to benefit of type checking on instance properties, you
need to use classes, interfaces and/or structural object types to constrain
the instance.
>
> If you want x to be a delegated and override-able "plain old" property, not a
> fixture, declare C thus:
>
> class C { prototype var x : String; }
>
To be clear, in the example above this would put a expando property x¹ on
f.__proto__.__proto__¹.
>
> Without prototype qualifying var, you get a fixture, and fixtures are always
> fixed as to meaning and type constraint by type of their containing class.
> That's their raison d'être.
>
> Having said that, I'll admit that your suggested change to the class
> instantiated by (new F) given F.prototype = new C is interesting and
> provocative. By default, ES4 as reference-implemented follows ES3 and makes
> (new F) for all functions F creates a new Object instance. But native
> constructor functions and classes can make instances of narrower types than
> Object, obviously (Date, RegExp, etc.). And some built-in classes (at least
> RegExp per ES3's spec, although no browsers follow this) have *prototype*
> properties of type Object.
>
> So there's an attractive symmetry in making F.prototype = new C cause (new F)
> to instantiate C instances.
>
> If we did this, you would still have fixtures overriding prototype properties,
> but you would have a fixture per (new F) instance, not one in the (new C)
> prototype instance as in your example (the one denoted by the variable |c|).
> That would avoid the pigeon-hole problem. (You could also use the prototype
> qualifier as in my counter-example.)
>
> If we did this, you might also (or might not) want |dynamic| in front of class
> C {...} in order to allow "expandos".
>
> Still thinking, comments welcome.
>
See my comment before my last one. Making prototypes more like classes would
make them less flexible, more complicated, and duplicate functionality that
is already available. I don¹t see the point, but maybe I¹m missing the use
case.
Jd
> /be
>
>
> _______________________________________________
> Es4-discuss mailing list
> Es4-discuss at mozilla.org
> https://mail.mozilla.org/listinfo/es4-discuss
------ End of Forwarded Message
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://mail.mozilla.org/pipermail/es-discuss/attachments/20070709/eab5225e/attachment-0002.html
On 7/2/07 1:29 PM, Brendan Eich wrote:
This behavior is incorrect. For compatibility with ES3, property writes must be shallow.
Agreed.
This seems a little too magic for me, and this is what classes are for. A delegate (i.e. proto) is just a delegate whose static type is always Object. If you want to benefit of type checking on instance properties, you need to use classes, interfaces and/or structural object types to constrain the instance.
To be clear, in the example above this would put a expando property x¹ on f.proto.__proto__¹.
See my comment before my last one. Making prototypes more like classes would make them less flexible, more complicated, and duplicate functionality that is already available. I don¹t see the point, but maybe I¹m missing the use case.
Jd
------ End of Forwarded Message