ES4 draft: Object initializers

# Lars Hansen (18 years ago)

I've attempted to sum up everything we have decided about object initializers (aka object literals). A short draft spec is included. Comments welcome from everyone, especially from ES4 WG members who might remember about things I've forgotten, or correct misunderstandings.

# liorean (18 years ago)

On 21/03/2008, Lars Hansen <lhansen at adobe.com> wrote:

I've attempted to sum up everything we have decided about object initializers (aka object literals). A short draft spec is included. Comments welcome from everyone, especially from ES4 WG members who might remember about things I've forgotten, or correct misunderstandings.

Just a few ideas, I think there's a few declarative features I'd like to see added:

  • I'd really like to see some declarative syntax for setting {DontEnum} flag on properties in object initialisers.

  • A declarative way to set catch-alls.

  • A declarative way to set the [[Call]] property.

  • And maybe declarative way to set the [[Prototype]] property. (This would of course be const.)

    var a={ dontenum toString: function()'[object customObj]' }, b={ [[Prototype]]: a, [[Call]]: function()..., get *: function(propname)..., set *: function(propname, value)..., ... };

The brackets for syntax would from what I can tell work here, since they are syntax errors in ES3. Or using some double keyword syntax I guess.

# Lars Hansen (18 years ago)

Thanks for pointing some of these out, Brendan did the same. This is already legal to define catch-alls:

{ var meta::get: function () { ... } }

Ditto for meta::set, meta::has, meta::delete, meta::invoke. A bit wordy though, and since it's already in (unless we special-case banish it for I don't know what good reasons) then we can probably support it with simple syntax, this is a suggestion:

{ get() { ... } }

As for dontenum, IMO use a namespace or a fixture. (If you're building a prototype object then presumably you'd like the methods not to be deletable as well; 'var' or 'const' is a cheap fix in that case.)

As for prototype, we're discussing it. A possible fix is to allow the name 'meta::prototype' to be used in the object initializer (only!) to signify initialization of the prototype. (You won't be able to read that value back with the syntax.)

# Jon Zeppieri (18 years ago)

On 3/20/08, Lars Hansen <lhansen at adobe.com> wrote:

Thanks for pointing some of these out, Brendan did the same. This is already legal to define catch-alls:

{ var meta::get: function () { ... } }

Is there a more recent version of the grammar than the docs at proposals:normative_grammar ?

This syntax isn't legal according to the grammar there, nor is it recognized by the RI.

# Brendan Eich (18 years ago)

On Mar 20, 2008, at 6:04 PM, Jon Zeppieri wrote:

On 3/20/08, Lars Hansen <lhansen at adobe.com> wrote:

Thanks for pointing some of these out, Brendan did the same. This is already legal to define catch-alls:

{ var meta::get: function () { ... } }

Is there a more recent version of the grammar than the docs at proposals:normative_grammar ?

Jeff Dyer will field this part, I'm sure.

This syntax isn't legal according to the grammar there, nor is it recognized by the RI.

See bugs.ecmascript.org/ticket/225 and please fix this if you
are willing ;-). I took a stab at adding a FIELD_NAME type, I can
mail you my patch if you are interested. It was a while ago and won't
merge perfectly. We need namespace-qualified property names in object
initialisers for record types as well as object initialisers, for
iterators and generators.

# Jeff Dyer (18 years ago)

On 3/20/08 7:00 PM, Brendan Eich wrote:

On Mar 20, 2008, at 6:04 PM, Jon Zeppieri wrote:

On 3/20/08, Lars Hansen <lhansen at adobe.com> wrote:

Thanks for pointing some of these out, Brendan did the same. This is already legal to define catch-alls:

{ var meta::get: function () { ... } }

Is there a more recent version of the grammar than the docs at proposals:normative_grammar ?

Jeff Dyer will field this part, I'm sure.

Only on my machine ;-). There needs to be a FieldKind production for 'var'. I'll post an updated grammar in the next few days.

# P T Withington (18 years ago)

I can ask o[f] !== undefined to determine if a defined property
named f exists in o, whether f is a fixture or not, correct? If
I only have o, is there a way for me to detect whether f is
implemented as a getter in o, or is that completely invisible to me?

# Lars Hansen (18 years ago)

o[f] !== undefined does not determine fixtureness, only whether f's there. The way to ask about fixtureness is by using a type, I think the following should work regardless of the actual type of x in o:

o is { x: * }

If you want to use a variable value f and not a constant name x then (perversely) I believe the following should work:

o is let (t = gensym()) global.eval("type " + t + " = { " + f + ":* }; return " + t, 4)

but you have to write your own gensym.

To my knowledge there is no way to ask whether something is a getter or setter. That said, the draft of the reflection API is not yet out and it seems like the kind of thing that it should possibly be able to reveal. (The reflection API should be able to answer the previous two questions too.)

# Lars Hansen (18 years ago)

A bug: repeated fields are also disallowed if the object initializer has a type annotation.

In summary, only object initializers that look like ES3 initializers are allowed to have repeated fields (for backward compatibility).

# Lars Hansen (18 years ago)

Another feature I forgot to include is ticket #370, the type annotation on an object initializer can be a nominal type in some cases. (The trac seems to be down, so I can't quote details. Anyhow I'm not sure if this ticket was ever discussed in a meeting, so it remains speculative.)

# Lars Hansen (17 years ago)

And finally a couple of items that came up in discussion at the last face-to-face. (Mark Miller's idea, somewhat generalized.)

(1) 'const' assigns the manifest type of value stored to the the property that is created, ie, this expression is true:

   { const x: "foo" } is { x: string }

(2) 'const' can prefix the entier object literal, meaning that all properties in the literal are 'const':

   const { x:"foo", y:true } is { x:string, y:boolean }

(3) The prefix form can also be applied to array literals:

   const ["foo",true] is [string,boolean]

This last bit makes it less painful to use arrays for multiple 
return values ("tuples") because the literal does not have to
be annotated with the type:

   function f(): [string,boolean] {
      ...
      return const ["foo",true]
   }

IMO it ought to be possible to use 'var' in those same ways but we didn't discuss that much (if at all).

# Mark S. Miller (17 years ago)

On Mon, Apr 7, 2008 at 10:21 AM, Lars Hansen <lhansen at adobe.com> wrote:

IMO it ought to be possible to use 'var' in those same ways but we didn't discuss that much (if at all).

I don't understand. What would it mean?

# Brendan Eich (17 years ago)

var (outside of eval, an ES1 flaw) means DontDelete.

# Mark S. Miller (17 years ago)

On Mon, Apr 7, 2008 at 2:47 PM, Brendan Eich <brendan at mozilla.org> wrote:

var (outside of eval, an ES1 flaw) means DontDelete.

Excellent!

# Lars Hansen (17 years ago)

Here is the second draft, which incorporates most things discussed so far. Changelog near the beginning, with an OPEN ISSUES section.

# Lars Hansen (17 years ago)

-----Original Message----- From: es4-discuss-bounces at mozilla.org [mailto:es4-discuss-bounces at mozilla.org] On Behalf Of Lars Hansen Sent: 7. april 2008 16:20 To: es4-discuss at mozilla.org Subject: RE: ES4 draft: Object initializers

Here is the second draft, which incorporates most things discussed so far. Changelog near the beginning, with an OPEN ISSUES section.

One facility that was introduced in this draft copies the type tags of 'const' and 'var' attributed properties into the type of the object, for "ease of use". As a consequence, this test is true:

{ const x: "foo" } is { x: string }

I am going to remove that facility again because it violates the "explicit is better than implicit" principle. IMO the programmer should state her intent:

{ const x: "foo" } : { x: string }

Type definitions help make this somewhat less wordy. And with or without the type annotation x would still be a read-only fixture, as desired, but the type of the x property would be "*", not "string".

(It's easy to say that when the literals are simple then the types are obvious, but once they involve more complicated expressions I'm guessing the gains are illusory.)

# Mark S. Miller (17 years ago)

On Tue, Apr 8, 2008 at 12:20 PM, Lars Hansen <lhansen at adobe.com> wrote:

One facility that was introduced in this draft copies the type tags of 'const' and 'var' attributed properties into the type of the object, for "ease of use". As a consequence, this test is true:

I don't understand how this could work for "var".

{ const x: "foo" } is { x: string }

I am going to remove that facility again because it violates the "explicit is better than implicit" principle. IMO the programmer should state her intent:

{ const x: "foo" } : { x: string }

A possible counter-argument is that the type of "foo" is string rather than *. We don't make the programmer write

"foo" :string

in order to get the right type.

(It's easy to say that when the literals are simple then the types are obvious, but once they involve more complicated expressions I'm guessing the gains are illusory.)

That's not clear to me. The cases that come to mind compose well:

{ const x: { const y: "bar" } }

has type { x: { y: string }}

# Lars Hansen (17 years ago)

-----Original Message----- From: Mark S. Miller [mailto:erights at google.com] Sent: 8. april 2008 15:38 To: Lars Hansen Cc: es4-discuss at mozilla.org Subject: Re: ES4 draft: Object initializers

On Tue, Apr 8, 2008 at 12:20 PM, Lars Hansen <lhansen at adobe.com> wrote:

One facility that was introduced in this draft copies the type tags

of 'const' and 'var' attributed properties into the type of the object, for "ease of use". As a consequence, this test is true:

I don't understand how this could work for "var".

The type is introduced on the fixture based on the initial value; subsequent values will be constrained to be of that type. In terms of Draft 2,

obj = { var x: "foo" } // same as { x:"foo" } : { x:string } obj.x = "bar" // ok obj.x = 37 // not ok

{ const x: "foo" } is { x: string }

I am going to remove that facility again because it violates the "explicit is better than implicit" principle. IMO the programmer should state her intent:

{ const x: "foo" } : { x: string }

A possible counter-argument is that the type of "foo" is string rather than *. We don't make the programmer write

"foo" :string

in order to get the right type.

It's true, we don't. But object initializers evaluate to new compound objects every time, not to immutable values that are ===, so I'm skeptical about the analogy. The type of an ES3 object initializer is 'Object' (not *). And if all the constituents of the initializer are literal expressions it's easy enough to see what's going on. But not if the constituents are not literal expressions.

(It's easy to say that when the literals are simple then the types are obvious, but once they involve more complicated expressions I'm guessing the gains are illusory.)

That's not clear to me. The cases that come to mind compose well:

{ const x: { const y: "bar" } }

has type { x: { y: string }}

I'm more concerned about cases like this:

{ const x: f() }

which are quite opaque and where the object will have a type that depends on the value returned by f, possibly a different type every time the initializer is evaluated. It's not the unbounded number of types that worries me but the fact that the type of that expression is at the mercy of f.