Block exprs as better object literals (was: Semantics and abstract syntax of lambdas)

Mark S. Miller erights at google.com
Mon Jan 5 04:15:40 PST 2009


On Mon, Dec 22, 2008 at 1:35 PM, Yuh-Ruey Chen <maian330 at gmail.com> wrote:

> TBH, even after substituting more Java-esque keywords like "public" for
> "reveal", this just doesn't appeal to my aesthetic sense. The user will
> naturally wonder why he should do |reveal (privX)| rather than |return
> privX|. I'm not sure what the benefit there is to trying to completely
> replace functions with lambdas for class methods.
>

Rather than agree or disagree with this, I'd like to separate issues. The
previous message bound together two separable ideas: 1) The notion of
"blocks with revealed values" as a TCP amenable way to introduce lambdas
while dodging Waldemar's leakage hazard. 2) Enhanced blocks with exported
declarations as a better object literal, adapting ideas from Peter Michaux's
<https://mail.mozilla.org/pipermail/es-discuss/2008-November/008185.html>.

Here, I'll make a revised proposal involving only #2. It will make no use of
lambda or reveal, and desugar only to ES3.1 and non-controversial elements
of ES-H (const and let declarations) + a placeholder "let" expression syntax
for turning a block into an expression without violating TCP. Again, I focus
on abstract syntax and am sloppy with the details of concrete syntax. In the
absence of another "reveal" concenpt to harmonize with, I will also go back
to use of the familiar "public".

Without "lambda", I take no position as to how one concretely turns a block
into an expression without violating TCP. I would still like to see a
TCP-respecting "lambda" and "let", but I'm trying to separate these issues
as much as I can.


# a restatement of the (abstracted) status quo
<block> ::= "{" <statement>* "}"
<statement> ::=
|        <declaration>
|        <expr>
|        <otherStatement> # like if, ...
<declaration> ::=
         "var" <ident> ("=" <expr>)?
|        "const" <ident> (":" <expr>)? "=" <expr>
|        "let" <ident> (":" <expr>)? "=" <expr>
|        "function" <ident> <paramList> <block>

# the new stuff
<expr> ::= ...
|        "object" ("implements" <expr>) <objectBody>
|        "let" "{" <statements>* <expr> "}" # Evaluates to the value of the
last <expr>
<objectBody> ::= "{" (<statement> | <member>)* "}"
<member> ::=
         "public" <declaration>
|        "public" <ident> (":" <expr>)? "=" <expr>
|        "public" <ident> <paramList> <block>

The sugared form of the previous class-like example, expressed in this
language, becomes

const Point = Object,freeze(function(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;
  };
});


If we wish, we can still introduce a further "class" syntax which desugars
to a frozen function returning an object:

<declaration> ::= ...
|        "class" <ident> <paramList> <objectBody>

So we could write the above as

class Point(privX, privY) {
  let privInstVar = 2;
  ...
}

Both of these desugar to

const Point = Object,freeze(function(privX, privY) {
  return let {
    let privInstVar = 2;
    const privInstConst = -2;
    const toString = Object.freeze(function() { return ('<' + getX() + ',' +
getY() + '>'); });
    const getX = Object.freeze(function() { return privX; });
    const getY = Object.freeze(function() { return privY; });
    let pubInstVar = 4;
    const pubInstConst = -4;
    Object.freeze(Object.create(Point.prototype, {
      toString: {value: toString},
      getX: {value: getX},
      getY: {value: getY},
      pubInstVar: {get: function{return pubInstVar;}, enumerable: true},
      pubInstConst: {value: pubInstConst, enumerable: true}
    }))
  };
});

My previous message mentioned 4 issues it left unaddressed:
a) decent concrete lexical syntax
b) how can we name the "self" object being initialized?
c) how can we provide an alternative to Object.prototype to inherit from?
d) how can we simply express revealing a setter as well?

On #a, in the absence of "lambda", I think the above concrete sugared syntax
is good, modulo the need to decide on an actual concrete syntax for turning
blocks into TCP-respecting expressions.

On #b, you just say
  const self = object .. { .. };
The const read barrier has exactly the correct effect: An object can define
functions that refer to itself during initialization, but it cannot actually
use itself during initialization. Further, two objects defined in the same
enclosing scope can refer to each other without any imperative nonesense to
set up the cycle.

On #c, I revived the earlier notion of "implements" to show how it can be
done in the context of this proposal.

#d remains unresolved. I didn't like anything I came up with.

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


More information about the Es-discuss mailing list