Classes as Sugar -- old threads revisited
Cormac Flanagan
cormac at cs.ucsc.edu
Tue May 26 14:49:39 PDT 2009
Mark,
Thanks for clarifying this design.
One important issue to consider is the goal of classes as
high-integrity, unforgeable objects.
It seems the desugaring of "class Foo" to "object implements Foo"
loses this ability ...
> class Foo(p1, p2) { ... }
>
> desugars to
>
> const Foo(p1, p2) {
> return object implements Foo { ... };
> }
since other code can later do:
object implements Foo { ... weird behavior here ... }
Is there some way to strengthen this behavior?
(some other comments are included below.)
- Cormac
>
> which desugars to
>
> const Foo = Object.freeze(function Foo(p1, p2) {
> return object implements Foo { ... };
> });
Plus:
Object.freeze(Foo.prototype);
right?
>
> As with ConstFunctionDeclaration, if ObjectDeclaration is not accepted
You mean ObjectExpression, IIUC.
> into ES-Harmony, then consider it only an explanatory device.
>
> ObjectBody:
> Statement
> | Declaration
> | "public" Declaration
> | "public" Identifier (":" Expression)_opt "=" Expression
> | "public" Identifier "(" FormalParameterList_opt ")" "{" FunctionBody "}"
>
> where
>
> public x :T = y;
>
> desugars to
>
> public const x :T = y;
>
> and
>
> public foo(p1, p2) { body; }
>
> desugars to
>
> public const foo(p1, p2) { body; }
>
> Revisiting Peter's example,
>
> class Point(privX, privY) {
> let privInstVar = 2;
> const privInstConst = -2;
> public toString() {
> return ('<' + getX() + ',' + getY() + '>');
> };
> public getX() { return privX; };
> public getY() { return privY; };
> public let pubInstVar = 4;
> public pubInstConst = -4;
> }
>
> desugars to
>
> const Point(privX, privY) {
> return object implements Point {
> let privInstVar = 2;
> const privInstConst = -2;
> public toString() {
> return ('<' + getX() + ',' + getY() + '>');
> };
> public getX() { return privX; };
> public getY() { return privY; };
> public let pubInstVar = 4;
> public pubInstConst = -4;
> };
> });
>
> which desugars to a hoisted
>
> const Point = Object.freeze(function(privX, privY) {
> return object implements Point {
> let privInstVar = 2;
> const privInstConst = -2;
> public const toString() {
> return ('<' + getX() + ',' + getY() + '>');
> };
> public const getX() { return privX; };
> public const getY() { return privY; };
> public let pubInstVar = 4;
> public pubInstConst = -4;
> };
> });
> Object.freeze(Point.prototype);
>
> The remaining elements needing explanation, ObjectExpression,
> ObjectBody, and
>
> "public" Declaration
>
> desugar together into a LetExpression whose final expression is
> created by gathering together representatives of the "public"
> declarations. The intent of the "implements" clause is that the value
> of the object expression be tagged somehow with an unforgeable nominal
> type, such that this value is able to pass the corresponding
> guard. For now, I will take a shortcut and assume that when a
> guard-value is a function, that the dynamic type-like test is approx
>
> isFrozenProp(guard, 'prototype') && (specimen instanceof guard)
>
> If the function's 'prototype' property is frozen, then instanceof is
> at least a monotonic test. However, it is effectively forgeable -- it
> guarantees no useful property -- since anyone may create an object
> that passes this test but has arbitrarily weird behavior. (Thanks to
> Waldemar for emphasizing this point at our last meeting.) In order to
> have a high integrity desugaring of ClassDeclaration or
> ObjectExpression, we need better lower level support for some kind of
> trademarking mechanism. We will need to revisit this issue, but not in
> this note.
>
> With this caveat, our example further desugars to
>
> const Point = Object.freeze(function(privX, privY) {
> return let {
> // hoisted functions first
> const toString = Object.freeze(function() {
> return ('<' + getX() + ',' + getY() + '>');
> });
> Object.freeze(toString.prototype);
> const getX = Object.freeze(function() { return privX; });
> Object.freeze(getX.prototype);
> const getY = Object.freeze(function() { return privY; });
> Object.freeze(getY.prototype);
>
> let privInstVar = 2;
> const privInstConst = -2;
> let pubInstVar = 4;
> const pubInstConst = -4;
>
> Object.freeze(Object.create(Point.prototype, {
> toString: {value: toString},
> getX: {value: getX},
> getY: {value: getY},
> pubInstVar: {get: Object.freeze(function{return pubInstVar;}),
> enumerable: true},
> pubInstConst: {value: pubInstConst,
> enumerable: true}
> }))
> };
> });
> Object.freeze(Point.prototype);
>
> Actually, I cheated above. Notice the lack of an "enumerable: true" in
> the properties representing toString, getX, and getY. Rather than
> consider
>
> "public" Identifier "(" FormalParameterList_opt ")" "{" FunctionBody "}"
>
> equivalent to
>
> "public" "const" Identifier ...
>
> consider it instead to be almost identical but representing a method
> definition. As a method definition, it makes sense (to me at least) to
> suppress its enumerability.
>
> By considering this syntactic form to represent a distinct method
> definition production, we can almost cleanly address another of
> Waldemar's concerns. Within the FunctionBody of a method production,
> we can rename all free "this"s to refer to the object being made. For
> example
>
> object { public getMe() { return this; }}
>
> could desugar to
>
> let {
> const t1 = object { public getMe() { return t1; }};
> t1
> }
>
> where t1 is a variable name not otherwise used in the
> ObjectExpression. This would desugar to
>
> let {
> const t1 = let {
> const getMe = Object.freeze(function getMe() { return t1; });
> Object.freeze(getMe.prototype);
> Object.freeze(Object.prototype, {
> getMe: {value: getMe}
> })
> }
> t1
> }
>
> The remaining problem left unaddressed by this proposal is that it
> creates an unmet need for an analogous private method production,
> where "this" is analogously renamed.
>
> --
> Cheers,
> --MarkM
> _______________________________________________
> Es-discuss mailing list
> Es-discuss at mozilla.org
> https://mail.mozilla.org/listinfo/es-discuss
>
More information about the es-discuss
mailing list