Classes as Sugar -- old threads revisited

Cormac Flanagan cormac at cs.ucsc.edu
Tue May 26 14:56:11 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