Closed domenic closed 10 years ago
@erights I do not like the name very much. Perhaps we can bikeshed it here to avoid flooding es-discuss. (Sorry Brendan for tagging you in in the OP; please feel free to mute the thread.)
Consider:
Array.from(iterable)
: "Array from iterable"Array.of(element)
: "Array of element"vs.
Promise.as(thenable)
: "Promise as thenable"In fact it seems like this is almost the opposite of what we mean.
Suggestions welcome. Tagging in @kriskowal for his thoughts. I don't have any short ideas; Promise.toPromise
is the best I can think of, unless we can get consensus on changing Array.from
to behave like we want Promise.from
to behave.
@domenic Sigh. I see what you mean. Perhaps there's another short word we haven't thought of yet?
I imagine we have a deliberate blind spot over Promise.for(value)
, but we are going for promise.catch
, right?
We can always break out the old preposition list: http://www.englishclub.com/grammar/prepositions-list.htm
"via" seems moderately attractive.
@kriskowal That could work, but the fact that Promise.for(promise) === promise
is a little bit weird. (In case it wasn't clear from the OP, this operation is equivalent to IsPromise(x) ? x : Promise.resolve(x)
. Promise.resolve
doesn't work because it should probably create a new promise whenever you call it.)
"Moderately attractive" conveys my feelings on Promise.via(thenable)
exactly.
Promise.to(eatVegetables())
reads alright.
OMG
Current contenders: Promise.to
and Promise.via
. Let's see how they fare:
Promise.to(5)
"Promise to 5"Promise.to(thenable)
"Promise to thenable"Promise.to(promise)
"Promise to promise"vs.
Promise.via(5)
"Promise via 5"Promise.via(thenable)
"Promise via thenable"Promise.via(promise)
"Promise via promise"OK. But now let's take a page from @kriskowal's book and consider some more realistic scenarios.
var data = getData(); Promise.to(data).then(...)
Promise.to(getData()).then(...)
var request = $.ajax(...); Promise.to(request).then(...)
Promise.to($.ajax(...)).then(...)
vs.
var data = getData(); Promise.via(data).then(...)
Promise.via(getData()).then(...)
var request = $.ajax(...); Promise.via(request).then(...)
Promise.via($.ajax(...)).then(...)
.Hrm. I don't feel any more enlightened.
I think "to" works if you read it the other way around.
"5 to Promise", "thenable to Promise", etc. Makes perfect sense.
We should consider "from" to still be a candidate. Let's try it:
Promise.from(5)
"Promise from 5"Promise.from(thenable)
"Promise from thenable"Promise.from(promise)
"Promise from promise"var data = getData(); Promise.from(data).then(...)
Promise.from(getData()).then(...)
var request = $.ajax(...); Promise.from(request).then(...)
Promise.from($.ajax(...)).then(...)
.And using the Promise constructor without "new" as a verb:
Promise(5)
"Promise 5"Promise(thenable)
"Promise thenable"Promise(promise)
"Promise promise"var data = getData(); Promise(data).then(...)
Promise(getData()).then(...)
var request = $.ajax(...); Promise(request).then(...)
Promise($.ajax(...)).then(...)
.Altogether, I like "to" best, but "via" and "from" are ok. The Promise constructor as coercer reads best, but I agree with Brendan that we should not add more such double-duty constructors when we don't need to.
@annevk If we could read these backwards, we should revive "as". But we can't.
It's a bit unfortunate that Promise.to(x)
means more or less the same operation as Array.from(x)
, despite "to" and "from" being linguistically opposite.
Yeah, that disqualifies it IMO.
I think I'm coming around to Promise.from :(.
Why is Promise.of() wrong?
Also, do we need the Promise.something(promise) === promise behavior?
Cop-out and leave this to libraries? (Doesn't really seem acceptable.)
@annevk Promise.of isn't wrong, it's just not needed in the subset that also omits .flatMap. The difference is indeed the Promise.something(promise) === promise behavior, we do need it, and we should not leave it to libraries, because .then-level programmers, in a full AP2 system will use Promise.of where they should be using Promise.something. This is exactly the issue you reminded me that I'd reversed myself on.
The reason is that the layers of wrapping that accumulate, if Promise.something(promise) !== promise, accumulate once per iteration on many natural patterns of tail-recursive async loop, such as the old reference implementation of Q.async at the bottom of http://wiki.ecmascript.org/doku.php?id=strawman:async_functions#reference_implementation . (This needs to be updated for the new ES6 generator API.)
Looking through synonyms. Promise.like() maybe... Promise.from() seems fine though. We could just point out that unlike Array it does sometime return its argument. Or modify Array to do the same.
I could live with Promise.from(promise)
and live with the minor inconsistency with Array.from(array)
. Array.from
should make a copy because changes to the result have the observable side-effect of corresponding changes to the input. With promises, there should be no observable difference. (To that end, we will need to keep a side-table of thenables that have been coerced so we can consistently return the same real-promise for a given thenable and avoid attempting and possibly failing to restart a lazy thenable promise)
@kriskowal I buy this as a good enough excuse for keeping Promise.from and not advocating that Array.from be changed from the current always copy behavior to the less useful conditional copy behavior. Any dissent to declaring Promise.from as the winner? If not, we should take this back to es-discuss.
Implemented as Promise.from
, opening #12 to track consensus on that name.
I personally think Array.from
behaving the same would be useful for its typical use cases, for the same reason of allocation being costly. The always-create-fresh use case can be served by [...maybeArrayMaybeNot]
, but Array.from
would normally be used to ensure that what you get is an array before doing things to it. I guess those things could be mutation-ey things, but normally they're going to be filter
, map
, etc.
Also, not to rock the mini-consensus we've got going on here, but we could also do Promise.when(x)
as the name, if that doesn't carry too much historical baggage :). Just throwing it out there!
I've never liked when
. It reads really strangely if it's not immediately followed by a .then
Promise(foo)
++. I'm not sure from
makes sense if Promise.from(promise) === promise
.
Domenic Denicola wrote:
Implemented as |Promise.from|, opening #12 https://github.com/domenic/promises-unwrapping/issues/12 to track consensus on that name.
I personally think |Array.from| behaving the same would be useful for its typical use cases, for the same reason of allocation being costly. The always-create-fresh use case can be served by |[...maybeArrayMaybeNot]|, but |Array.from| would normally be used to ensure that what you get is an array before doing things to it. I guess those things could be mutation-ey things, but normally they're going to be |filter|, |map|, etc.
We shouldn't go in circles. Array.from exists to support polyfillability, which [...arrayish] does not. It must make a copy, always, because arrays are mutable and returning the argument sometimes but not others creates pigeon-hole problem bugs. Finally, "from" does not connote a no-op as a preposition -- it suggests an implicit verb, "create" or "transfer" or "reconstruct" before the "from".
What to call the idempotent project-as-promise function is a good question, but I don't think it is Promise.from.
/be
Brendan Eich wrote:
What to call the idempotent project-as-promise function is a good question, but I don't think it is Promise.from.
Mark urged me to suggest a name:
Promise.cast(x)
/be
Summary of my reactions at this point:
I can live with cast
.
cast
is OK. I'd like to reiterate @kriskowal's point from earlier though, that Promise.from(promise)
returning promise
is more OK than Array.from(array)
returning array
because even if it did return a new promise, that new promise would be indistinguishable from the original except by ===
.
Domenic Denicola wrote:
|cast| is OK. I'd like to reiterate @kriskowal https://github.com/kriskowal's point from earlier though, that |Promise.from(promise)| returning |promise| is more OK than |Array.from(array)| returning |array| because even if it did return a new promise, that new promise would be indistinguishable from the original except by |===|.
I hear you on that one. We could use Promise.from and mostly get away with it. But the connotation problem I mentioned still exists, and some foolish consistency deduction still hurts. Neither of those problems afflicts Promise.cast.
/be
That's fair.
On the other hand, can we use Kris's "it doesn't matter very much" argument to make Promise.resolve
not always return a new promise? I am not sure how I feel about that. But, it is notably what Q.resolve
and RSVP.resolve
already do.
@domenic I don't understand your question. I just looked at https://github.com/kriskowal/q/wiki/API-Reference and I don't see any Q.resolve. Do you mean the call behavior of Q called as a function?
Right, we alias Q.resolve to Q; in the Q 0.8 timeframe Q was not a function and that's what we called it. We left it in Q 0.9 and haven't deprecated it officially (i.e. using it doesn't cause deprecation warnings).
@domenic Ok, with that clarified, I still don't understand the question. Could you try to restate in other words? Thanks.
Sure.
Tab's original objection to Promise.resolve(truePromise) === truePromise
was that the static methods should be treated as factories, and Promise.resolve(x)
should desugar obviously to new Promise(resolve => x)
. I find this argument compelling, until I consider that a new promise resolved to an existing promise behaves exactly the same as the original promise, as Kris pointed out, modulo ===
. Combined with the fact that Q.resolve(qPromise) === qPromise
and RSVP.resolve(rsvpPromise) === rsvpPromise
, I am asking that we reconsider letting Promise.resolve(promise) === promise
.
Perhaps I'm not getting it because I never found that argument compelling. IIUC, you agree that
Promise.someNameWeNeedToChoose(promise) === promise
must be true. You are suggesting we consider "resolve" for that name. Is this right?
If so, I prefer .cast for two reasons:
OK, that's fair. Dropping it :).
Ok, any objections to closing this local bikeshed with .cast and taking the issue back to es-discuss?
Let's do that.
Nobody feels strongly that
Promise(x)
should coerce, and @BrendanEich at least dislikes this.See http://esdiscuss.org/topic/killing-promise-fulfill#content-55