The new operator
Axel Rauschmayer wrote:
Until ES.next, I’ve stuck to the simple rule: Always use
new
when you want to create an instance. That avoids confusion when it comes to functions such as String and Boolean: I like using them to coerce values.
User-defined constructors are often coded so that users must invoke via new. This may happen, therefore it does happen, and it won't go away. I don't know why you are treating ES.next as some slate-cleaning exercise, below ("Do we still need ..."). The answer by default is "yes".
Given that value object constructors such as uint64 are currently invoked without
new
, I’m wondering whether that rule changes:
- Do we still need
new
? If we had class declarations, their desugared versions could easily always create new instances, whether invoked vianew
or as a function. The same holds for object exemplars: Calling such an exemplar could “instantiate” it.
Protocols are fine and perhaps a new-free one will take over some years hence, but not soon.
- Should there be alternate, possibly less confusing, ways of coercing values? ToPrimitive() would certainly be nice to have.
An object may need to be coerced to a number of value types. How would ToPrimitive know which one to use? There may be a default, but not a unique target type.
Axel Rauschmayer wrote:
Until ES.next, I’ve stuck to the simple rule: Always use
new
when you want to create an instance. That avoids confusion when it comes to functions such as String and Boolean: I like using them to coerce values. User-defined constructors are often coded so that users must invoke via new. This may happen, therefore it does happen, and it won't go away. I don't know why you are treating ES.next as some slate-cleaning exercise, below ("Do we still need ..."). The answer by default is "yes".
Right. Sorry if I gave off that impression. I’m mostly thinking about how to keep JavaScript easy to teach. And with value object constructors we now have three ways of creating instances (new Foo(...), Foo(...), foo(...)). If we want to reduce the number of ways, there are two possibilities: (1) Advocate that people use the new
operator less. (2) Write uint64(123) as new UInt64(123) or as UInt64(123). With literal syntax such as 123L, decision (2) might not even matter much.
Another possibility is that we want to emphasize that value object constructors are a different beast. Then using lowercase names can provide a good visual clue.
In other words: I’m trying to figure out how value object constructors fit into the current picture.
Avocating against the use of “new” is both a bad idea (not clear enough) and a source of problem :
For exemple, in Chrome (latest version), you get some weird things:
XMLHttpRequest() TypeError: DOM object constructor cannot be called as a function. Best , François
From: Axel Rauschmayer Sent: Sunday, May 13, 2012 9:53 PM To: Brendan Eich Cc: es-discuss Subject: Re: The new operator Axel Rauschmayer wrote:
Until ES.next, I’ve stuck to the simple rule: Always use `new` when you want to create an instance. That avoids confusion when it comes to functions such as String and Boolean: I like using them to coerce values.
User-defined constructors are often coded so that users must invoke via new. This may happen, therefore it does happen, and it won't go away. I don't know why you are treating ES.next as some slate-cleaning exercise, below ("Do we still need ..."). The answer by default is "yes".
Right. Sorry if I gave off that impression. I’m mostly thinking about how to keep JavaScript easy to teach. And with value object constructors we now have three ways of creating instances (new Foo(...), Foo(...), foo(...)). If we want to reduce the number of ways, there are two possibilities: (1) Advocate that people use the new
operator less. (2) Write uint64(123) as new UInt64(123) or as UInt64(123). With literal syntax such as 123L, decision (2) might not even matter much.
Another possibility is that we want to emphasize that value object constructors are a different beast. Then using lowercase names can provide a good visual clue.
In other words: I’m trying to figure out how value object constructors fit into the current picture.
Axel Rauschmayer wrote:
Right. Sorry if I gave off that impression. I’m mostly thinking about how to keep JavaScript easy to teach. And with value object constructors we now have three ways of creating instances (new Foo(...), Foo(...), foo(...)).
Again, nothing is new about value object constructors. Most constructors can be called as functions (not via new) to construct. With value objects, coercing (converting) and constructing are indistinguishable because identity is by value not reference.
(BTW as dherman and markm have pointed out, this means value objects must not be usable as WeakMap keys.)
If we want to reduce the number of ways, there are two possibilities: (1) Advocate that people use the
new
operator less.
One size does not fit all, in practice and even in theory.
(2) Write uint64(123) as new UInt64(123) or as UInt64(123).
There are no such UInt64, etc. capitalized names (If there were, probably just the U and not the adjacent I would be capitalized). There is no need for an analogue of Number given int64.prototype and uint64.prototype.
With literal syntax such as 123L, decision (2) might not even matter much.
That's the idea.
Another possibility is that we want to emphasize that value object constructors are a different beast. Then using lowercase names can provide a good visual clue.
That is intentional too.
In other words: I’m trying to figure out how value object constructors fit into the current picture.
Value objects are new under the sun. They are not the same as the primitive AKA value types built into JS. They are typeof-type "object" but equal by value not reference (under the hood there's a pointer-compare fast path, of course). They are frozen so you can't decorate them with expandos to break this equivalence.
More significantly than typeof-type, value objects may be falsy, e.g. 0L and uint64(0). This is a necessary part of the design.
Teaching JS always involves subsetting. The worst thing to do is mislead adult students that a subset is the whole thing, but even adults start with subsets, and different folks will chose to program in different subsets, even when they've mastered the full language.
For now value types must be built in but the design allows everything including operators and literal suffixes to be user-defined. More on this in a bit.
Brendan Eich wrote:
Again, nothing is new about value object constructors. Most constructors can be called as functions (not via new) to construct.
Emphasis on "constructors" here. The novelty with value objects is not in constructors callable without 'new', but in what I went on to write:
Value objects are new under the sun. They are not the same as the primitive AKA value types built into JS. They are typeof-type "object" but equal by value not reference (under the hood there's a pointer-compare fast path, of course). They are frozen so you can't decorate them with expandos to break this equivalence.
Hope this is clear. To your point, and as Francois posted, teaching people to leave out 'new' is not going to work in various cases. Better to teach subsets starting with ES5 strict or smaller, and build up from there (including XHR at some point). A fair amount (not all) of ES6 should be "more advanced", not "beginner".
In general, trying to teach new stuff that's optional-by-design (remember, 1JS) as if it were usable exclusive of old stuff looks like a mistake to me.
If there's a great ES6->ES5 transpiler (traceur is shaping up nicely,
thanks Arv) then perhaps. That is still in the future, in part because ES6 is not done yet, nor is traceur -- and nor is experience with such a "JS pedagogy" built on a future-subset-first teaching approach.
Finally, and Axel used ES.next clearly but I used ES6 so I'm clarifying my own post: value objects are not in ES6.
Value objects are being prototyped in order to implementor- and user-test, so they're on the Harmony agenda as a strawman proposal. For SpiderMonkey, I will avoid exposing them to web content in shipping (not nightly/aurora/beta) Firefox releases until they are promoted to harmony:proposals status.
Between value objects, Emscripten, and *JS (mbebenita.github.com/Mvm) it's an exciting time to be pushing low-level JS performance and language extensions!
On 13 May 2012 13:57, Brendan Eich <brendan at mozilla.org> wrote:
Protocols are fine and perhaps a new-free one will take over some years hence, but not soon.
- Should there be alternate, possibly less confusing, ways of coercing
values? ToPrimitive() would certainly be nice to have.
An object may need to be coerced to a number of value types. How would ToPrimitive know which one to use? There may be a default, but not a unique target type.
Vaguely-related use-case, mentioned only so that TC-39 et al get end-user feedback :)
In GPSEE (a server-side embedding of SpiderMonkey with a focus on POSIX) we have a number of data types which share a common type of backing store....we call them "ByteThings", and, really, they are just C pointers + size, and the JS object wrappers wind up being a sort of memory access protocol.
When you have C-stuff, and in particular an FFI that can return pointers to JS space, you find that, at some point, you need to cast. We decided to arrange our ByteThing constructors so that they create new ByteThings when invoked with the new operator, and otherwise treat their argument sort of like a coercion source. We call this "ByteThing casting". So we can write code ~ like this:
var mem = new Memory(2); mem[0] = 65; mem[1] = 0; var sb = new MutableStruct("struct stat"); /* Make a new object that maps properties to a struct stat */ stat("filename", sb); print(Memory(sb).asString()); print(mem.asString());
which is basically
char *mem = malloc(2); mem[0] = 65; mem[1] = 0; struct stat sb; stat("filename", &sb); printf("%s\n", (char *)sb): printf("%s\n", mem);
Clearly this is a contrived example, but -- being able to have |new Memory()| and |Memory()| mean different, but related, things is useful to us. We could solve the same class of problems without the new operator by overloading based on argument type, but the code would be a little less legible and harder to reason about if the argument types weren't apparent at the callsite.
Value objects are new under the sun. They are not the same as the primitive AKA value types built into JS. They are typeof-type "object" but equal by value not reference (under the hood there's a pointer-compare fast path, of course). They are frozen so you can't decorate them with expandos to break this equivalence.
More significantly than typeof-type, value objects may be falsy, e.g. 0L and uint64(0). This is a necessary part of the design.
That makes sense. They are very close to primitives. With universal value object types, one could in principle replace primitives with value objects (that is neither a suggestion nor a wish of mine ;-).
Continuing the previous idea of a “fixed” typeof as a (shimmable) function, such a function could return
- "null"
- "undefined"
- "boolean"
- "number"
- "string"
- "value object"
- "reference object"
That would cover my use cases.
Hope this is clear. To your point, and as Francois posted, teaching people to leave out 'new' is not going to work in various cases. Better to teach subsets starting with ES5 strict or smaller, and build up from there (including XHR at some point). A fair amount (not all) of ES6 should be "more advanced", not "beginner".
In general, trying to teach new stuff that's optional-by-design (remember, 1JS) as if it were usable exclusive of old stuff looks like a mistake to me.
If there's a great ES6->ES5 transpiler (traceur is shaping up nicely, thanks Arv) then perhaps. That is still in the future, in part because ES6 is not done yet, nor is traceur -- and nor is experience with such a "JS pedagogy" built on a future-subset-first teaching approach.
Right. Initially, I’d teach ES5 and a “what’s new in ES6”. Later, I would initially omit some ES5 things, e.g.: Start with let
and explain var
and IIFEs much later.
Instead of subsetting, I prefer “simple (possibly even simplistic) rules” for beginners. For example, they can afford to initially ignore ASI and always use semicolons. And they can afford to initially ignore == and always use ===. I use such rules for myself, too. It allows me to stay sane w.r.t. to some of the details.
Finally, and Axel used ES.next clearly but I used ES6 so I'm clarifying my own post: value objects are not in ES6.
Value objects are being prototyped in order to implementor- and user-test, so they're on the Harmony agenda as a strawman proposal. For SpiderMonkey, I will avoid exposing them to web content in shipping (not nightly/aurora/beta) Firefox releases until they are promoted to harmony:proposals status.
Between value objects, Emscripten, and *JS (mbebenita.github.com/Mvm) it's an exciting time to be pushing low-level JS performance and language extensions!
I like that they can be introduced in a stepwise fashion (whenever that may be...): Numbers first, user-defined value object types later. Numbers should complement the binary data proposal nicely.
Axel Rauschmayer wrote:
Value objects are new under the sun. They are not the same as the primitive AKA value types built into JS. They are typeof-type "object" but equal by value not reference (under the hood there's a pointer-compare fast path, of course). They are frozen so you can't decorate them with expandos to break this equivalence.
More significantly than typeof-type, value objects may be falsy, e.g. 0L and uint64(0). This is a necessary part of the design.
That makes sense. They are very close to primitives. With universal value object types, one could in principle replace primitives with value objects (that is neither a suggestion nor a wish of mine ;-).
Continuing the previous idea of a “fixed” typeof as a (shimmable) function, such a function could return
- "null"
- "undefined"
- "boolean"
- "number"
- "string"
- "value object"
- "reference object"
That would cover my use cases.
Indeed the primal sin in JS, a combination of "make it look like (and in Netscape 3 interface with [LiveConnect with] Java)" and me being in a hurry and taking shortcuts, is primitive types that are not rationalized up front as objects.
We can fix over time, though. Let typeof wither (it will die hard) and promulgate a constructor.name-based string-valued "type" function. But as Allen and I keep saying, avoid the nominal-type-overquerying anti-pattern, per Smalltalk.
Until ES.next, I’ve stuck to the simple rule: Always use
new
when you want to create an instance. That avoids confusion when it comes to functions such as String and Boolean: I like using them to coerce values.Given that value object constructors such as uint64 are currently invoked without
new
, I’m wondering whether that rule changes:Do we still need
new
? If we had class declarations, their desugared versions could easily always create new instances, whether invoked vianew
or as a function. The same holds for object exemplars: Calling such an exemplar could “instantiate” it.Should there be alternate, possibly less confusing, ways of coercing values? ToPrimitive() would certainly be nice to have.
Axel