How much sugar do classes need?

Mark S. Miller erights at google.com
Sat Nov 22 15:35:24 PST 2008


In the "Look ma, no this" thread <
https://mail.mozilla.org/pipermail/es-discuss/2008-August/thread.html#6941>,
I presented a desugaring of classes without proposing what the sugared
syntax should be. This desugared form is based on the objects-as-closure
style pioneered by Crock. The resulting thread explored some possible
sugarings as well as alternative desugarings. At <
http://wiki.ecmascript.org/doku.php?id=strawman:classes>, Cormac presents an
approach to the sugar that we discussed at the Kona EcmaScript meeting. Some
details aside, this looks like it could all be desugared straightforwardly
to the objects-as-closure style.

However, in the (small number of days) since Kona, I've become less happy
with the degree of sugar involved. For some elements of the desugared form,
the proposed sugar is significantly more convenient, but not for others.
When the sugar doesn't add enough convenience to pay for its complexity, we
should leave it out. Unnecessary sugar obscures what's "really" going on
(one level of abstraction down), making it harder to form a good cognitive
model of the language. (As Alan Perlis says, "To much syntactic sugar leads
to cancer of the semicolon.")

Here's an only slightly sweetened alternative. I hope it doesn't leave a
bitter aftertaste. Let's start with a desugared example expressing most of
the features that the sugared class propossal wishes to address:

  const Point = let {
    // after desugaring
    let privClassVar = 1;
    const privClassConst = -1;
    Point.pubClassConst = -3;

    function Point(x, y) {
      let privInstVar = 2;
      const privInstConst = -2;

      const self = Object.create(Point.prototype);
      Object.defineProperties(self, {
        toString: {value: Object.freeze(function() {
          return '<' + self.getX() + ',' + self.getY() + '>';
        })},
        getX: {value: Object.freeze(function() { return x; })},
        getY: {value: Object.freeze(function() { return y; })},
        pubInstVar: {value 4, writable: true, enumerable: true},
        pubInstConst: {value: -4, enumerable: true},
      });
      Object.preventExtensions(self);
      return self;
    }
    Object.freeze(Point);
  };

Note that the freezing of Point also freezes Point.prototype, so afterwards
"x instanceof Point" is a sound and monotonic nominal type check. Whether "x
:Point" should therefore do an instanceof check I will leave to a separate
discussion.

Sugaring *only* the elements above that need it, one possibility is:

    // before desugaring
    class Point(x, y) {
      static {
        let privClassVar = 1;
        const privClassConst = -1;
        Point.pubClassConst = -3;
      }
      let privInstVar = 2;
      const privInstConst = -2;

      public self implements Point {
        to toString() {
          return '<' + self.getX() + ',' + self.getY() + '>';
        },
        to getX() { return x; },
        to getY() { return y; }
        let pubInstVar: 4,
        const pubInstConst: -4
      };
      return self;
    }

EcmaScript historically started by trying to unify the notions of scoped
variables and inherited properties. This was a noble effort, but failed. At
every step, we found that this attemped unification led to trouble. My main
discomfort with the proposed sugar on the wiki page is that it would obscure
this distinction yet again. Above I have tried to present a minimalist sugar
by example that is no less convenient, but which always makes visible in the
sugared form the distinction between variables and properties in the
desugared form.

-- 
   Cheers,
   --MarkM
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20081122/8a015779/attachment.html>


More information about the Es-discuss mailing list