Promise() vs. new Promise()

# Axel Rauschmayer (10 years ago)

Currently there seem to be two ways to create promises. Normal classes throw an exception if you call them as functions (without new). Should Promise do the same?

Axel

# Alex Kocharin (10 years ago)

An HTML attachment was scrubbed... URL: esdiscuss/attachments/20140820/b27adb0c/attachment

# Rick Waldron (10 years ago)

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). Should Promise do the same?

They already do: people.mozilla.org/~jorendorff/es6-draft.html#sec-promise Steps 1-4.

# Axel Rauschmayer (10 years ago)

Ah, true. Cool, thanks.

# Andrea Giammarchi (10 years ago)

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

# Andrea Giammarchi (10 years ago)

throw whenever a constructor is meant to be used as such

without using new

# Boris Zbarsky (10 years ago)

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.

# Andrea Giammarchi (10 years ago)

Yep, I was rather talking about a more generic approach with constructors, I've picked the wrong one for the example.

Thanks for clarification

# Alex Kocharin (10 years ago)

An HTML attachment was scrubbed... URL: esdiscuss/attachments/20140820/bf0b1d9f/attachment

# Claude Pache (10 years ago)

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). So new 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.

# Alex Kocharin (10 years ago)

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). So new 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.

# Alex Kocharin (10 years ago)

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 uses new 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. :(

# Brendan Eich (10 years ago)

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.

# Allen Wirfs-Brock (10 years ago)

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 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.

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 referencesMaprather 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

# Andrea Giammarchi (10 years ago)

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 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.

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 referencesMaprather 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

# Andrea Giammarchi (10 years ago)

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.

# Andrea Giammarchi (10 years ago)

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 ^_^

# Salvador de la Puente González (10 years ago)

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);?

# Andrea Giammarchi (10 years ago)

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

# Tab Atkins Jr. (10 years ago)

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 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]

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.

# Andrea Giammarchi (10 years ago)

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.

# C. Scott Ananian (10 years ago)

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 writing var 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.)

# Allen Wirfs-Brock (10 years ago)

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)

# Andrea Giammarchi (10 years ago)

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 ?