Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Terminology #5

Closed
domenic opened this issue Oct 25, 2012 · 27 comments
Closed

Terminology #5

domenic opened this issue Oct 25, 2012 · 27 comments

Comments

@domenic
Copy link
Member

domenic commented Oct 25, 2012

Getting the terminology standardized would be really nice. Currently, the state of the art among Promises/A-compliant implementers is:

  • fulfilled: a promise state wherein a fulfillment value is available
  • rejected: a promise state wherein a rejection reason is available
  • resolved: a promise state encompassing both fulfilled and rejected
  • pending/unresolved: the initial promise state, when the promise is neither fulfilled nor rejected.

Furthermore, these implementers generally also include the following resolver-related verbs:

  • resolve(otherPromise): a verb, wherein the resolver tells its promise to take on the (possibly eventual) state of otherPromise.
  • reject(reason): a verb, wherein the resolver tells its promise to become rejected with reason.

As is, Promises/A+ switches from rejected to broken, and settles on "pending" while leaving any mention of "unresolved" or "resolved" out.

I kind of like this, as it allows us to separate the promise states {fulfilled, broken, pending} from the resolver verbs {resolve, reject}.

Compare this to the current status quo: the promise states are {fulfilled, rejected, unresolved} whereas the resolver verbs are {resolve, reject}. There is a great symmetry between reject and rejected, but that is deceptive, because resolve and resolved are almost entirely unrelated. (That is, you can resolve with an unresolved promise, which leaves the resolver's promise as "unresolved".) See also kriskowal/q#122 and the discussion contained therein.


Alternately, we could try to just standardize on the existing terminology, since there may not be much gain in changing. In that case we could leave attempting to fix the resolver/promise terminology mismatch to an eventual Resolvers/A+ spec.

@briancavalier
Copy link
Member

briancavalier commented Oct 25, 2012

It's probably obvious, but I do like separating the states from the verbs, mostly because of the resolve (verb) vs "resolved" (state) mismatch. I prefer broken over rejected. For whatever reason, rejected makes me think "I chose not to accept your promise", rather than "I chose never to fulfill my promise". I prefer pending over unresolved because unresolved is the obvious opposite of resolved, and I'd rather not use resolved as a state name.

I think the impact of going with these state names, even though they are different from current "state of the art", is relatively minimal from a code perspective. Changing resolver verbs seems much more invasive.

If we end up in a situation where the promise states are fulfilled and broken, and the resolver verbs are resolve and reject, do you think that will cause more confusion?

@juandopazo
Copy link
Contributor

juandopazo commented Oct 26, 2012

I think those states are good for the spec but don't need to be reflected exactly in the implementations

@lsmith
Copy link
Contributor

lsmith commented Oct 26, 2012

I've now written and deleted three separate responses. resolve(otherPromise) really messes with the terminology. I say use {fulfilled, rejected, pending} as promise states and leave the verbs for Resolvers/A+.

I prefer broken over rejected. For whatever reason, rejected makes me think "I chose not to accept your promise", rather than "I chose never to fulfill my promise".

I agree with you semantically, but I don't see a reason to deviate from the status quo here. resolve() has become vague, but if there were a fulfill() verb that didn't special case promise values, there would be state-verb parity, allowing resolve() and "resolved" to function in terms of the superstate. But that's Resolvers/A+ stuff (maybe it doesn't even make sense).

I prefer pending over unresolved because unresolved is the obvious opposite of resolved, and I'd rather not use resolved as a state name.

I have no qualms about using "unresolved" as meaning "not fulfilled or broken/rejected", but because resolve(otherPromise) would suggest transitioning away from an "unresolved" state, I agree that "pending" is preferable. Ah, the hairs we split.

Changing resolver verbs seems much more invasive.

Agreed that changing verbs would be more invasive.

@lsmith
Copy link
Contributor

lsmith commented Oct 26, 2012

And now after writing that response, I read the linked Q issue thread. I really need to follow links sooner :) I was also thinking about isPending() vs isResolved().

@domenic
Copy link
Member Author

domenic commented Oct 26, 2012

It sounds like the question really comes down to "broken" vs. "rejected". My move toward "broken" is motivated by the resolver issue, as I've explained, but I can also see the value in not changing existing terminology. I won't hold fast on either direction.

I'd like to hear from @wycats and @kriskowal on this one.

@lsmith
Copy link
Contributor

lsmith commented Oct 26, 2012

I'm also not passionate about "rejected" over "broken". I like the semantics of "broken", but don't want to ruffle feathers unnecessarily. I'll go either way.

@kriskowal
Copy link
Member

kriskowal commented Oct 26, 2012

Before we synthesize something, here’s some more analysis. @douglascrockford uses a different set of terms in https://github.com/douglascrockford/monad/blob/master/vow.js

  • promise -> vow
  • fulfill -> keep
  • reject -> break
  • not resolved -> pending
  • resolve -> herald and enlighten (both private)
  • defer -> make
  • all -> every
  • allResolved ~> any

"keep" and "break" are the natural conclusive states for a "promise" as far as the English language is concerned. "make" is the right verb for "making a promise", though not as good for making that which "Q.defer()" returns, regardless of what it’s called.

@kriskowal
Copy link
Member

kriskowal commented Oct 26, 2012

Also, "resolve" has been called "become". It’s a good word.

@kriskowal
Copy link
Member

kriskowal commented Oct 26, 2012

I can’t speak for the original authorial intent of Tyler Close for the ref_send lib.Q API, but a good reason to avoid keep and break was that break was a keyword. If you ditch "keep", you need terminology that doesn’t imply the existence of "keep", ergo "resolve" and "reject".

It’s still a good reason for any promise library that needs to be used in a JavaScript engine that doesn’t allow keywords for property names. That said, a forward-looking library would use a lot of keywords.

@domenic
Copy link
Member Author

domenic commented Oct 27, 2012

Maybe I've just been staring at JavaScript-related promises for so long, but to me "you should fulfill your promise" seems just as natural (if perhaps a bit more formal) as "you should keep your promise". Hrm.

@domenic
Copy link
Member Author

domenic commented Oct 27, 2012

Also, I really like "become" as a resolver [sic] verb.

@briancavalier
Copy link
Member

briancavalier commented Oct 27, 2012

I like the make/keep/break triad a lot. It's short, natural, obvious, all one syllable. It's a shame it has the break keyword problem--seems like we can't go down that road yet. Also, I think make is slightly problematic in that it implies making the "thing to the left of the dot". For example, crockford's vow.make() works great, but when.make() doesn't work for me, and neither does Q.make().

I like "become" because it's also simple and obvious. Only two concerns: 1) people may take it to mean than just adopting promise state+value/reason, and 2) it sounds less "final" than "resolve", so folks might think you can do it more than once. Documentation can probably solve both.

"fulfill" is just as natural for me as "keep", and "filfill a promise" is listed as a synonym for "keep a promise" in a few places.

Another one I just ran across: "honour", "honor"

I still prefer "broken" over "rejected", but "break" is a problem, and I understand the "broken implies the existence of kept" argument. So, the question there is whether the states need to match the resolver verbs, or can/should they be independent. I honestly don't know what's best there.

At least right now, after a couple beers, I could live with:

promise states:
pending, fulfilled, rejected

resolver verbs:
fulfill(value), become(promiseOrValue), reject(reason)

promise creation (map nicely to promise states):
pending(), fulfilled(value), rejected(reason)

... not sure about a good word for creating one promise from another, i.e. the creational analog of become? "like(promise)", "from(promise)"??

@juandopazo
Copy link
Contributor

juandopazo commented Oct 27, 2012

FYI Futures in Scala: http://docs.scala-lang.org/overviews/core/futures.html

@briancavalier
Copy link
Member

briancavalier commented Oct 27, 2012

Cool, thanks for the pointer @juandopazo. The "successful" and "failed" terms work well for describing a task, but not necessarily for describing a promise, imho. Makes sense in the Scala case since it all sounds more task-oriented. Interesting info around the Try[T] monad as well.

The term "future" is kind of nice, although I'm not sure how we'd incorporate it ... maybe it's an alternative to the creational "pending()"?

There's also Twisted Deferred. From what I understand, Dojo's original Deferred impl was nearly a direct port of Twisted's. I'm not really crazy about the terminology, tho.

@kriskowal
Copy link
Member

kriskowal commented Oct 27, 2012

The way I see it, the globally optimal vocabulary is "promise", "keep", "break", "notify", and "transfer".

However, "break" is the fly in the ointment. The locally optimal vocabulary includes "promise", "fulfill", and "reject".

I don’t think we’ve yet seen a good option for "defer" or "make", or a good name for a promise and resolver pair. "defer" isn’t great, but is’t horrible. "make" collides with another primitive in Q that is used to make promises for any of the states, including proxies for remote objects.

We haven’t seen a good option for "resolve" either. It might be okay to have "fulfill", "reject", "transfer", and "coerce", to separate the last two responsibilities of modern "resolve". Thus, when a callback returns a value that might be a promise to keep working, the mechanism could look liked resolver.transfer(coerce(returned)). Restructuring the solution instead of the name might be more illuminating. This has the interesting implication that returning fulfill(promise) would treat the promise as a fulfilled value, which might even be the key to solving a problem I’ve had with my implementation of remote object proxy promises, which never be unwrapped locally.

@kriskowal
Copy link
Member

kriskowal commented Oct 27, 2012

One possible insight is that the "defer()" method is not necessarily called by the entity "making the promise" (in the English sense). It could be thought of as an arbiter or witness, who creates the "entangled pair" of "promise" and "keeper/breaker/notifier-of-progress". The person "making the promise" (in the English sense) gets the "resolver" and the person who has been given a promise — gets the promise. The "resolver" represents "responsibility".

Naming is hard.

@briancavalier
Copy link
Member

briancavalier commented Oct 28, 2012

@kriskowal lots of great stuff in your last two posts!

I like the idea of "transfer"/"coerce" to separate the behaviors. "Transfer the responsibility of fulfilling or rejecting" seems like a nice way to think about it. I also like the "arbiter" way of thinking about the {promise, resolver} pair. That's how I tend to think about it, but hadn't thought of the word "arbiter"--it's is a good one.

When/if we get into creating a Resolvers/A+, I think it'll be great to separate the resolver as a passable object, and to explore the transfer/coerce idea more.

To bring this conversation back to Promises/A+: if we were to agree that the verbs "fulfill" and "reject" seem like a good way forward for Resolvers/A+, then it makes sense to me to go with "fulfilled" and "rejected" as the state names for Promises/A+. That only leaves the "pending" vs. "unresolved" decision. If we ditch "resolve" in favor of "transfer"+"coerce", then "pending" seems like the right choice.

How does everyone feel about going with "pending", "fulfilled", and "rejected" for state names in Promises/A+?

@domenic
Copy link
Member Author

domenic commented Oct 28, 2012

I'm +1 on either pending/fulfilled/rejected or pending/fulfilled/broken.

(Aside: I think naming the callbacks onFulfilled etc. instead of just fulfilled etc. would make things slightly more clear.)

@briancavalier
Copy link
Member

briancavalier commented Oct 28, 2012

Hmmm, good point. I like fulfilled and rejected better, but for the sake of clarity (i.e. not confusing people between state names and callback names), onFulfilled and onRejected might be better.

@lsmith
Copy link
Contributor

lsmith commented Oct 29, 2012

+1 on {pending, fulfilled, rejected} per my prior comment.

briancavalier added a commit that referenced this issue Oct 29, 2012
…ed/onRejected as callback names to avoid confusion with fufilled/rejected state names
@briancavalier
Copy link
Member

briancavalier commented Oct 29, 2012

b002072 incorporates the latest terminology suggestions. Please have a read through, and let me know what you think, and if I missed anything.

@juandopazo
Copy link
Contributor

juandopazo commented Oct 29, 2012

{pending, fulfilled, rejected} looks very good. You missed the change to onFulfilled on the code examples :P

@briancavalier
Copy link
Member

briancavalier commented Oct 29, 2012

@juandopazo indeed! search and replace fail. I knew you guys would find my mistakes :) Thanks

@domenic
Copy link
Member Author

domenic commented Oct 29, 2012

A search within the document for "br" leaves the following snippet:

fulfilling or breaking them.

Otherwise I think you got them all!

@briancavalier
Copy link
Member

briancavalier commented Oct 29, 2012

Thanks @domenic

@domenic
Copy link
Member Author

domenic commented Oct 29, 2012

I think this is closeable now; all future terminology discussions belong in the realm of Resolvers/A+. (Whose very name will depend on such discussions, heh.)

@briancavalier
Copy link
Member

briancavalier commented Oct 29, 2012

Onward to #7

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants