Promise() vs. new Promise()
An HTML attachment was scrubbed... URL: esdiscuss/attachments/20140820/b27adb0c/attachment
On Wed, Aug 20, 2014 at 8:52 AM, Axel Rauschmayer <axel at rauschma.de> wrote:
Currently there seem to be two ways to create promises. Normal classes throw an exception if you call them as functions (without
new
). ShouldPromise
do the same?
They already do: people.mozilla.org/~jorendorff/es6-draft.html#sec-promise Steps 1-4.
Ah, true. Cool, thanks.
Alex I think the new direction is to throw whenever a constructor is meant
to be used as such. If you don't want to use new you can var p = Object.create(Promise.prototype); Promise.call(p, executor);
which is
"yack" if you ask me
throw whenever a constructor is meant to be used as such
without using new
On 8/20/14, 9:45 AM, Andrea Giammarchi wrote:
Alex I think the new direction is to throw whenever a constructor is meant to be used as such. If you don't want to use new you can
var p = Object.create(Promise.prototype); Promise.call(p, executor);
This won't work correctly, since 'p' won't have the right internal slots. So people.mozilla.org/~jorendorff/es6-draft.html#sec-promise step 3 will throw.
Yep, I was rather talking about a more generic approach with constructors, I've picked the wrong one for the example.
Thanks for clarification
An HTML attachment was scrubbed... URL: esdiscuss/attachments/20140820/bf0b1d9f/attachment
Le 20 août 2014 à 16:56, Alex Kocharin <alex at kocharin.ru> a écrit :
But... why?
I mean, every constructor can determine if it is called without
new
(that "this instanceof" check on top of every other constructor). Sonew
keyword can really be removed from everywhere except in constructors themselves.Using
new
does create issues. For example, you can't write "new require('something').Constructor(blah)", and I don't even remember how to put brackets right.Why make people use it?
According to esdiscuss.org/notes/2013-07-25 (search for "anti-pattern"), allowing constructors without new
would break subclassing.
20.08.2014, 19:18, "Claude Pache" <claude.pache at gmail.com>:
Le 20 août 2014 à 16:56, Alex Kocharin <alex at kocharin.ru> a écrit :
But... why?
I mean, every constructor can determine if it is called without
new
(that "this instanceof" check on top of every other constructor). Sonew
keyword can really be removed from everywhere except in constructors themselves.Using
new
does create issues. For example, you can't write "new require('something').Constructor(blah)", and I don't even remember how to put brackets right.Why make people use it?
According to esdiscuss.org/notes/2013-07-25 (search for "anti-pattern"), allowing constructors without
new
would break subclassing.
Still doesn't make much sense... This pattern (this instanceof ...) breaks another pattern (BaseClass.call(this)). Why first one is deprecated, not the second one?
I mean, I was able to subclass such a class with prototype injection without any issues, and it feels more natural in javascript after all.
20.08.2014, 22:47, "Brendan Eich" <brendan at mozilla.org>:
Alex Kocharin wrote:
Still doesn't make much sense... This pattern (this instanceof ...) breaks another pattern (BaseClass.call(this)). Why first one is deprecated, not the second one?
Fair point, and a topic at the last TC39 meeting was to finally add a way in JS to tell whether your constructor function was called via new or directly.
I mean, I was able to subclass such a class with prototype injection without any issues, and it feels more natural in javascript after all.
You mean using
new
? Mileage varies, but I agree that client code that usesnew
well can add clarity about costs and reference uniqueness (lack of aliasing).
I mean, not using new
. JS is a functional language after all, and having a function that can't be called as a function is weird.
If constructor supports calling it without new, I can pass it around as a function, do this for example:
> ['foo', 'bar'].map(Error)
[ [Error: foo], [Error: bar] ]
With mandatory new
this would be much less elegant.
I was about to construct realistically-looking chain with [fn1, fn2, fn3].map(Promise).forEach(addThenHandler), but FF already seem to throw on it. :(
Alex Kocharin wrote:
I mean, not using
new
. JS is a functional language after all, and having a function that can't be called as a function is weird.
Gotcha, and my point was not to mandate new
, just allow it. I thought
we relaxed things so new
was optional if there was no issue leaving it
out:
esdiscuss.org/topic/make-class-constructors-work-with-call-too
But that was a while ago.
If constructor supports calling it without new, I can pass it around as a function, do this for example:
> > ['foo', 'bar'].map(Error) [ [Error: foo], [Error: bar] ]
With mandatory
new
this would be much less elegant.
Agreed, mandating new
where there's no issue (legacy, e.g., Date; or
other?) would be Bad(tm).
I was about to construct realistically-looking chain with [fn1, fn2, fn3].map(Promise).forEach(addThenHandler), but FF already seem to throw on it.:(
Hrm.
On Aug 20, 2014, at 1:11 PM, Brendan Eich wrote:
Alex Kocharin wrote:
I mean, not using
new
. JS is a functional language after all, and having a function that can't be called as a function is weird.Gotcha, and my point was not to mandate
new
, just allow it. I thought we relaxed things sonew
was optional if there was no issue leaving it out:esdiscuss.org/topic/make-class-constructors-work-with-call-too
But that was a while ago.
If constructor supports calling it without new, I can pass it around as a function, do this for example:
> > ['foo', 'bar'].map(Error) [ [Error: foo], [Error: bar] ]
With mandatory
new
this would be much less elegant.Agreed, mandating
new
where there's no issue (legacy, e.g., Date; or other?) would be Bad(tm).I was about to construct realistically-looking chain with [fn1, fn2, fn3].map(Promise).forEach(addThenHandler), but FF already seem to throw on it.:(
Hrm.
We've talked about this several times at TC39 meetings and the consensus has always been to not conflate the meaning of new C() and C() for new built-in constructors.
The problem is that is difficult to correctly code (in ES code) a constructor function that has this behavior. Even assuming there is a way to reliably determined if you were called with or without new
you still need to have to separate logic paths for the two cases and there are other subclassing issues (more below). For these reasons, we should expect that most constructors defined using class
will require use of new
to instantiate them.
If as matter of principle, we shouldn't be specifying built-ins that do things in a way that is difficult to reproduce in ES code and we shouldn't be encouraging usage patterns that confuse people or are error prone to implement.
If you want a purely functional way to do instantiation, then I suggest adding a new
method to Function.prototype:
Function.prototype = function new(...args) {return new this(...args);
Back to subclassing, assume that Map was implemented in ES, something like this: ( called-via-new is a placeholder for new syntax) ` class Map { constructor(...args) { if (! called-via-new) return new Map(...args); // initialize this using args } //methods }
//then the following do the same thing:
var m = Map();
var m = new Map;
You might then, reasonably expect to code
class ExtendedMap extends Map {
additionalMethod() {}
}
but if you do, you will discover:
var m = ExtendedMap(); //actually creates an instance of Map
var m = new ExtendedMap; //actually creates an instance of ExtendedMap;
the problem is that the Map constructor explicitly references
Maprather than the actually invoked constructor and there is no good may to determine what the actual constructor was. So to make the subclass work correctly you have to remember to code the subclass as:
class ExtendedMap extends Map {
constructor(...args) {
if (! called-via-new) return new ExtendedMap(...args);
return new super(...args);
}
additionalMethod() {}
}
`
and repeat a pattern like this in every other subclass you write. Much better to just avoid this sort ot two-faced constructors.
Alen
I like the Function.prototype.new
hint but unless it's an implicitly
self bound method you still cannot easily pass it around as callback
for a map without binding it all over.
arr.map(MyClass.new)
without needing to explicitly bind it each time
is needed/used would be an exception, but probably a good one.
Sent from my Windows Phone From: Allen Wirfs-Brock Sent: 8/21/2014 1:19 To: brendan at mozilla.org Cc: es-discuss at mozilla.org Subject: Re: Promise() vs. new Promise()
On Aug 20, 2014, at 1:11 PM, Brendan Eich wrote:
Alex Kocharin wrote:
I mean, not using
new
. JS is a functional language after all, and having a function that can't be called as a function is weird.Gotcha, and my point was not to mandate
new
, just allow it. I thought we relaxed things sonew
was optional if there was no issue leaving it out:esdiscuss.org/topic/make-class-constructors-work-with-call-too
But that was a while ago.
If constructor supports calling it without new, I can pass it around as a function, do this for example:
> > ['foo', 'bar'].map(Error) [ [Error: foo], [Error: bar] ]
With mandatory
new
this would be much less elegant.Agreed, mandating
new
where there's no issue (legacy, e.g., Date; or other?) would be Bad(tm).I was about to construct realistically-looking chain with [fn1, fn2, fn3].map(Promise).forEach(addThenHandler), but FF already seem to throw on it.:(
Hrm.
We've talked about this several times at TC39 meetings and the consensus has always been to not conflate the meaning of new C() and C() for new built-in constructors.
The problem is that is difficult to correctly code (in ES code) a
constructor function that has this behavior. Even assuming there is a
way to reliably determined if you were called with or without new
you still need to have to separate logic paths for the two cases and
there are other subclassing issues (more below). For these reasons,
we should expect that most constructors defined using class
will
require use of new
to instantiate them.
If as matter of principle, we shouldn't be specifying built-ins that do things in a way that is difficult to reproduce in ES code and we shouldn't be encouraging usage patterns that confuse people or are error prone to implement.
If you want a purely functional way to do instantiation, then I
suggest adding a new
method to Function.prototype:
Function.prototype = function new(...args) {return new this(...args);
Back to subclassing, assume that Map was implemented in ES, something like this: ( called-via-new is a placeholder for new syntax) ` class Map { constructor(...args) { if (! called-via-new) return new Map(...args); // initialize this using args } //methods }
//then the following do the same thing:
var m = Map();
var m = new Map;
You might then, reasonably expect to code
class ExtendedMap extends Map {
additionalMethod() {}
}
but if you do, you will discover:
var m = ExtendedMap(); //actually creates an instance of Map
var m = new ExtendedMap; //actually creates an instance of ExtendedMap;
the problem is that the Map constructor explicitly references
Maprather than the actually invoked constructor and there is no good may to determine what the actual constructor was. So to make the subclass work correctly you have to remember to code the subclass as:
class ExtendedMap extends Map {
constructor(...args) {
if (! called-via-new) return new ExtendedMap(...args);
return new super(...args);
}
additionalMethod() {}
}
`
and repeat a pattern like this in every other subclass you write. Much better to just avoid this sort ot two-faced constructors.
Alen
For demo sake, I was talking about something like this in ES5:
Object.defineProperty(
Function.prototype,
'new',
{
configurable: true,
get: function() {
var Constructor = this;
Object.defineProperty(
Constructor,
'new',
{
value: function(/*...args*/) {
// ES6: return new Constructor(…args);
var
o = Object.create(
Constructor.prototype
),
r = Constructor.apply(o, arguments)
;
switch(typeof r) {
case 'undefined':
case 'boolean':
case 'number':
case 'string':
return o;
default: // including null
return r || o;
}
}
}
);
return Constructor.new;
}
}
);
// example
function A() {}
A.new() instanceof A; // true
P.S. please note, in case you do like above idea, some quirky engine will fail without weird tricks on lazy reassignment. I can write a more appropriate one if needed.
To this I'd like to add that in ES6 you don't need to explicitly invoke
inherited class constructor but rather use super
True is that this new
story makes Mixins like constructors more difficult
to use as classes becuase these cannot be invoked via .call
or .apply
directly and requires a very boring init()
like method.
Although there's nothing for mixins or their initialization in ES6 so it's OKish ...
Last, but not least, since you use instanceof, I'd like to underline in ES5 capable browser you have another pattern.
function SelfInstance() {'use strict';
return this || new SelfInstance;
}
no need to use the instanceof
check when this
could be undefined ^_^
The only problem I see here is you can not emulate completely new
with
ES. Why Promise add some internal slots when called with new and it does
not make the same when writing var p = Object.create(Promise.prototype); Promise.call(p, fn);
?
Agreed, and not only Promise.
AFAIK there is no way to detect if a generic constructor is a typeof
'class' or just a function, then invokable through a well known, commonly
used pattern as Generic.call(obj, p1, p2)
is when it comes to generics
initializations.
This is also a precedent that suggest Object.create(Generic.prototype)
is
useless when the Generic
is ES6 class due inability to initialize it
later on: a very "bad news" for prototypal inheritance.
Accordingly, interoperability with old code (the "one JS" myth) is compromised and prototypal inheritance won't be "fully portable" with user-land classes.
Latter is not necessarily unexpected since we know other cases that could fail, i.e.
var a = Object.create(Array.prototype);
Array.call(a, 1, 2, 3);
a; // still empty
Array(1, 2, 3); // [1,2,3]
but interoperability with user land classes, the inability to initialize an
object through one or more constructor composing behaviors, and the
inability to emulate new
does not make it look like a wise choice.
On the other hand, what is the advantage of knowing that new
was used, if
not for some syntax parser or JIT sake, if any?
Why anyone would like to be sure new
was used instead of Object.create
and why the former should keep failing with native and now with classes too?
Just curious to know who or what will end up benefiting this choice.
Thanks in advance for clarifications
Best
On Thu, Aug 21, 2014 at 2:03 AM, Andrea Giammarchi <andrea.giammarchi at gmail.com> wrote:
This is also a precedent that suggest
Object.create(Generic.prototype)
is useless when theGeneric
is ES6 class due inability to initialize it later on: a very "bad news" for prototypal inheritance.Accordingly, interoperability with old code (the "one JS" myth) is compromised and prototypal inheritance won't be "fully portable" with user-land classes.
Latter is not necessarily unexpected since we know other cases that could fail, i.e.
var a = Object.create(Array.prototype); Array.call(a, 1, 2, 3); a; // still empty Array(1, 2, 3); // [1,2,3]
I don't generally see this kind of "inheritance" used; it seems like a weird way to say "var a = new Array(1, 2, 3);".
I do commonly see code like:
function Foo() {
...
}
Foo.prototype = Object.create(Bar.prototype);
Which always works and will continue to work in the future.
It will not if you have
function Foo() {
Bar.call(this);
}
as generic way to extend classes in your old style logic, something that will inevitably fail the moment you do
Foo.prototype = Object.create(Bar.prototype);
with Bar
that comes from ES6 and it has been defined as class
instead
of old style function.
This is an interoperability problem where the new world cannot work in the
old one and having typeof Constructor === 'function'
means any old
inheritance based code won't even bother wrapping the .call behind a try
catch.
I hope I've described the problem in a better way ... this choice breaks badly old patterns and I am not sure yet I understand advantages about it.
Also, if you ask me, I think Array
example I proposed is a bad spec about
how the Array
behaves, and subclassing in general in ES3. We all know
that, now we have triple behavior in ES6.
On Thu, Aug 21, 2014 at 3:22 AM, Salvador de la Puente González <salva at unoyunodiez.com> wrote:
The only problem I see here is you can not emulate completely
new
with ES. Why Promise add some internal slots when called with new and it does not make the same when writingvar p = Object.create(Promise.prototype); Promise.call(p, fn);
?
Not true. See, for example:
paulmillr/es6-shim#170, paulmillr/es6-shim/blob/master/test/promise/subclass.js#L5, cscott/prfun/blob/master/lib/index.js#L520
It's super-annoying, but you can actually write ES5 code that will properly inherit the new internal slots in ES6.
I'm a big fan of Function.prototype.new
, though. And I think
Promise.new.bind(Promise)
is not all that terrible, if you want to
pass around a constructor as a function. (And there's another
es-discuss thread about trying to unify new
as an "ordinary"
function call in proxies as well.)
On Aug 20, 2014, at 7:30 PM, Andrea Giammarchi wrote:
I like the
Function.prototype.new
hint but unless it's an implicitly self bound method you still cannot easily pass it around as callback for a map without binding it all over.
arr.map(MyClass.new)
without needing to explicitly bind it each time is needed/used would be an exception, but probably a good one.
true, but you can write:
arr.map((...a)=>new Map(...a));
or define:
Object.assign(Function.prototype, { get asFactory() {return (...args)=>new this(...args)} }
so you can say:
arr.map(MyClass.asFactory)
I guess you haven't read the following code that does that in ES5 fashion :-)
mixing up syntax
Object.defineProperty(
Function.prototype,
'new',
{
configurable: true,
get: function () {
return Object.defineProperty(
this,
'new',
{value: (...args)=>new this(...args)}
).new;
}
}
);
But since most of the world of JS is scared to death since ES3 era to extend natives prototypes, wouldn't be nice to spec it as such natively in ES6 or ES7 ?
Currently there seem to be two ways to create promises. Normal classes throw an exception if you call them as functions (without
new
). ShouldPromise
do the same?Axel