Classes as Sugar -- old threads revisited

Mark S. Miller erights at
Mon Mar 30 19:40:29 PDT 2009

At the Harmony portion of the recent EcmaScript meeting, we took up
the classes as sugar discussion. For background, the relevant recent
threads on es-discuss are:

"Look ma, no this" thread starting at

"How much sugar do classes need?" thread starting at
especially Peter Michaux's

"Block exprs as better object literals" thread starting at
continuing in January starting at

The discussion was a bit frustrating because the committee was largely
unaware of the last two of these threads. Although I do recommend
reviewing them, I will here restate and refine my last suggestion in
a self contained pair of messages. Again, I would like to thank Peter
Michaux for the general approach.

Since the proposal I'm about to make will be in terms of a desugaring
to other elements of ES-Harmony, In this first message I will recap
and speculate on the needed elements of ES-Harmony. Since "lambda" is
still controversial, I will avoid it. But I will introduce instead the
minimal replacement I still need -- a "let" expression which respects
Tennent Correspondence. I would hope that this "let" actually desugars
to "lambda", but I do not assume this here. If we do adopt "lambda",
then all the desugaring I present to functions should instead be to

We need to refactor the ES5 grammar a bit. ES5 has two statement-level
declaration productions, VariableStatement and FunctionDeclaration. In
ES5, VariableStatement is included in Statement, whereas
FunctionDeclaration is included in SourceElement, which reads

  SourceElement: // ES5
  | FunctionDeclaration

The reason is that ES5 prohibits FunctionDeclarations in nested
blocks, permitting them only at the top level of Program and

Since ES-Harmony will allow lexically nested FunctionDeclarations as
well as "let" and "const" declarations, all with proper block-level
lexical scope, let's rename VariableStatement to VariableDeclaration
and refactor the grammar as:

  | FunctionDeclaration
  | ConstFunctionDeclaration
  | LetDeclaration
  | ConstDeclaration
  | ClassDeclaration

with ClassDeclaration explained in the next message. The important
point for now is that ClassDeclaration desugars to, in effect, declare
a function. Like a harmonious FunctionDeclaration, the name declared
by a ClassDeclaration has proper block-level lexical scope, and the
initialization of this name to the function is hoisted to the
beginning of the block so no uninitialized state is observable.

    "let" Identifier (":" Expression)_opt "=" Expression

    "const" Identifier (":" Expression)_opt "=" Expression

The optional (":" Expression) is for dynamic type checking, where the
expression is evaluated to a guard value in the current lexical scope,
and that guard value is somehow used to represent type-like
constraints on the values that may be bound to the variable it guards.

    // current Statement contents without VariableStatement

  | Declaration

    "{" SourceElements_opt "}"

    // current MemberExpression contents
  | LetExpression
  | ObjectExpression

with ObjectExpression explained in the next message.

    "let" Bindings_opt "{" SourceElements_opt Expression "}"

For purposes of this note, we can assume Bindings_opt is
absent. Likewise, this note has no need for a LetStatement.

The semantics of the LetExpression (due, IIRC, to a suggestion of Dave
Herman) is to evaluate the parts between the curlies as a nested
block, where the value of the LetExpression is the value of the
terminal Expression in its body.

The ConstFunctionDeclaration above elaborates on someone's suggestion,
I forget who, for a syntax like:

    "const" Identifier "(" FormalParameterList_opt ")" "{" FunctionBody "}"

This is just like the FunctionDeclaration syntax except for the use of
"const" in the position where "function" normally appears. Like a
FunctionDeclaration and a ClassDeclaration, a ConstFunctionDeclaration
declares a block-scoped hoisted function. For example,

  const foo(p1, p2) { body; }

desugars to

  const foo = Object.freeze(function foo(p1, p2) { body; });

where this pair is hoisted to the top of its enclosing block. It would
have been more elegant if we could have desugared directly to a
FunctionDeclaration, in order to reuse the latter's hoisting
machinery. However, we can't since the variable introduced by
FunctionDeclaration is mutable ("let"-like) and there's no way to get
the freezing of the function or its prototype to be automagically
hoisted as well. Hopefully the introduction of "lambda" will provide a
more elegant way to address this need. If neither lambda nor
ConstFunctionDeclaration are accepted into ES-Harmony, then consider
the latter as only an explanatory device for other desugarings to be
presented shortly.


More information about the Es-discuss mailing list