-
Notifications
You must be signed in to change notification settings - Fork 11
Why promises in the core at all? #21
Comments
I'd add that another argument against having promises in core is that it could require compromises that will make the situation actually worse than if core didn't simply do anything. Another argument for having promises is that the web platform has fully embraced promises and node not doing that would cause the platforms to eventually become too different. Implicit here is the assumption that a substantial contributor to node's success is the accessibility to web developers and similarity with web development. While technically both will be using "javascript" they will be so different that they might as well be different languages. |
I'd add that official support reduces friction in the Node ecosystem by removing the need for two package authors who both want to use promises to specify the specific shim, implementation, and version of promises they want to use before interoperating. |
It's also important to note promises are the language standard way to perform asynchronous computation. I understand that that does not make them the node standard way to do so - but getting consensus by TC39 is definitely not something easy - and we can dig a lot of discussion about why promises in JavaScript. Adoption by the DOM is also a plus IMO. As for against arguments - the fact postponing the discussion a year would let us observe how things like cancellation, async/await and others fan out. We're implementing a feature (promises in core) related to a feature (async/await) that is not yet standardized completely - if the standard changes (unlikely, but possible) we'll all eat our hats. |
@benjamingr do you know if we have any records of the discussions at TC39 regarding their embrace? That would probably be helpful to look at if it's available. |
Node.js currently stands on simple async functions that take callbacks, so probably it would be good to define first what's the difference between callback functions (in Node.js async function sense), and promises. In my eyes: Callback function is simplest possible low-level API for handling asynchronous calls, which turns to be difficult to use and maintain when used directly to deal with complex async flows. Promise is more high-level API for handling asynchronous calls, which when speaking of single async call brings more clutter than callback function, but it clearly stands out when we deal with complex async flows (and already was marked as standard for that). I'd say callback function API as a low-level one should never be compromised, however as in most cases when we work with Node.js we deal with complex async flows and want to use promises, a native promises support (as an addon) should be considered. The other and probably more blocking problem is that while we have There are different promise libraries that explore different approaches, and devs should be able to use Node.js with any of them. So if native promise support in Node.js cannot come with flexibility where developer can choose which promise implementation is used behind, then it probably should not land at all. |
@medikoo first of all thanks for stopping by! We'd love to have you participate in the discussions in this repo.
This is a very opinionated take. Many believe that it is not difficult to deal with callbacks, maintain code with them or deal directly with complex async flows with helper libraries like async.
I'm not sure promises are inherently higher level than callbacks. That's certainly not the case in some other languages. Also, whether or not it stands out when dealing with async flows is debatable and the goal of this repo is not to debate promises vs. callbacks it's about making lives easier for promise and callback users in Node in the light of promises.
The callback API is never going anywhere - it would literally break every single node program :) The goal of this proposal is to add promise support, we probably won't make an argument about how promises simplify things but rather only aim to support both user bases reasonably.
The things you find controversial are not what others fine controversial and vice versa. Lots of people are looking forward to rely on it. That said - no one is taking the ability to roll your own control flow anywhere. That is not even being considered. Again - the existing APIs are mostly frozen and no one is suggesting moving them anywhere. |
With regards to However, cancelation is a concern, depending on how it's implemented (at some point I saw discussion of cancelable promises being a subclass, which would be bad for our purposes.) What is the current state of discussion on cancelable promises? Are you concerned that they will move towards basing |
@chrisdickinson well, I don't want to sound like the broken record but that one guy working on that won't really be interested in our discussions at this point - so I can share what limited info I have:
|
Excellent, agreed. How do interested folks get involved?
Hm, but wouldn't it have to be the case that |
I tend to agree. It does however affect our ability to add the error recovery object parameter.
Well, http requests, socket writes, queued file operations and so on. |
About several months ago, i sent a mail to es-discuss to discuss about the possibility to reconsider promise as an Async primitive. The thread are getting rather furious, without getting any meaningful result, now i suppose this is the right place to discuss this matter? And i will keep as humble as possible in order to conduct my thought.
From what i understand, Promise is a proxy to a T typed value, which is produced in future or past.
So each Promise is a micro single time event queue, and the core of Promise is to focus on the state of value, to deal with async Error, a But Promise is not the only way to describe the dependence graph of asynchronous operations , Promise get widely accepted because its monodic interface, e.g. What i want to present here, is to use classical continuation construction to solve callback problem, which is widely used as control structures in other functional programming languages, see ConT/haskell, Continuation Monad/clojure. We even have a concrete proposal Composition Functions for ES.next, which i discovered after i roll my own implementation. Allow me to describe what continuation is, and why i think it's a better async primitive. A continuation is a function which consume a callback to give its value, what it abstract is not the value itself, but the action to produce this value. for example: (cb) => fs.readFile('...', (err, data) => cb(data)) is a continuation, it wait a callback to produce a value, if we want to add a monadic interface, we can just wrap it in an instance, let call it an Action(the name is not important): function Action(cont){
this.cont = cont;
} Now add a monadic compose is easy: Action.prototype.fmap = function(cb){
return new Action(
(_cb) => this._go((data) => _cb(cb(data)))
)
}
Action.prototype.mbind = function(cb){
return new Action(
(_cb) => this._go((data) => cb(data).cont(_cb))
)
} In fact it's a direct translation from haskell's ConT monad's fmap and bind: instance Functor (ContT r m) where
fmap f m = ContT $ \ c -> runContT m (c . f)
instance Monad (ContT r m) where
m >>= k = ContT $ \ c -> runContT m (\ x -> runContT (k x) c) And in my implementation i use Why this abstract important? because it capture the essence of continuation composition, and you can build any error handling scheme on top of it. While promise is a proxy to its value, Action is a wrapper to its underline action which produce this value. This will bring a whole possibilities to do retry/throttle/parallel/sequenece execution. If we add Promise into core, it means attaching a macro callback queue to every async value become a standard way, and accept its error handling scheme, i will strongly against this heavy weight solution. After all, even transformers package are not in haskell's base library. |
@winterland1989 Hey winterland - thank you for voicing your opinion.
Unfortunately, probably not. This is about providing Promise returning APIs in the standard installation of Node.js. es-discuss would be the correct place to drive ES language decisions (to the best of my knowledge).
No (popular) proposal for tighter integration of Promises in Node.js core involves touching the current APIs. All of the APIs in place today will still be available, and still accept callbacks. |
@rvagg had some good points on this threads question at #10 (comment) which are applicable here. Notably, it needs to be noted which for and against arguments are "promises in core" or just "promises in general" and to not conflate the two, as it does seem that some of the pros and cons already listed can equally be handled in user-land. User-land pros should not necessarily justify core changes. Core changes should be justified by arguments "better or worse in the core" or "must be or shouldn't in core". However, documenting pros and cons of promises in general is still valuable as they will still help influence and come up with more specific pros and con arguments for the core. |
@winterland1989 not at all, this is not the place to discuss these issues - esdiscuss is. Typically - they don't make up standard either - they check what solutions the community has already widely adopted (like promises - again - 20m downloads for node) and then discuss including them in the language. If you'd like to propose an additional primitive, first you'll have to actually write a userland solution and make sure people find it ergonomic and actually use it. (Also, of course a promise is a particular instance of a continuation with caching and error handling - that's the point) |
Ok, i understand it, so i leave my message as this: I think we should not make Promise into node's core because it's an opinionated solution to async and async error handling. It bundled with a error-handling scheme which not only affect origin throw semantics, but also have its own problem. Leave Promise as userland library will keep core minimal, which is a very good criteria.
It's a particular instance of EvenEmmiter, not a continuation, a continuation is about a action which happen in future, not the value it produced. It happen to have a monadic interface doesn't mean it's a monad. |
I do actually write a user land solution, see action-js, we use in our own company, i know its adoption is very low, but get larger adoption is another problem isn't it : ) |
http://blog.sigfpe.com/2008/12/mother-of-all-monads.html - although if you really want to have that argument go have it with headinthebox :P Of course, whether something is actually a monad or isn't (yours isn't technically either because it has a wrong signature for flatMap, like promises) is irrelevant here.
Yes, just like callbacks are an opinionated solution to async and async error handling. We're discussing adding promises to core because they're in the language standard and the biggest host environment (the DOM) has adopted them.
Well, actually I think it's the same problem - the language technical committee and node core both have an extremely high standard for inclusion. When we tried back in the 2000s to build standard based on what technical committees like and not what users wanted and found popular things didn't work out too well (for example - xhtml) and the community divided. The way to the standard today (or core) is to prove two things:
The reason is that different people have a very different opinion and perspective. The only way to move forward in what probably is the largest ecosystem in the world (JavaScript and Web Dev) is to adopt community and userland solutions. We used to be able to do that for library features (for example Array.prototype.map ) but now with babel we can do that for language features too. Also, it's not just "who has the best idea" it's also "who can put in the time to document it, test it, explain it and show its need. Hope that helps. In any case - I suggest we don't keep talking about your library here but you're welcome to bring it up in the appropriate channels like esdiscuss or better yet - build a userbase first :) |
I don't see any relation between Promise and this article, can you elaborate?
Fair enough, but at least let me give my attitude to the problem
|
@winterland1989 Promises are bundled with error-handling scheme for a good reason. Its because the language is bundled with that same error handling scheme. Callbacks and Action.js also come bundled with an error handling scheme. Its called "crash your process on every exception" and "code by pretending exceptions don't exist". This causes denial of service problems and loss of user work or data. Coding without exceptions can be done. It will require changing all synchronous functions to make them return Its not like in Haskell its considered acceptable for Analogously, async functions would then always return Oh and to get the same benefits as Haskell we would also need the ability to pattern match over The easier path, at least for the language was to keep exceptions (somewhat flawed as they are) and provide an async analog. I do believe that it will be unwise for ES7 to not improve exceptions by at least adding first class |
I can't follow you here, Let's use haskell as example, in haskell we use an algebra type, such as
In haskell there's no ReferenceError or TypeError ... They are statically checked, we have type level nature number to encode But in javascript these errors are not avoidable, that's why sometime you want distinguish Programmer Errors from Operational Errors, Promise should never deal with Programmer Errors, but it does so. Is adding implicity
Analogously, we should at least emulate algebra types by using T :: Error | OtherType, that's what Action.js do : )
In fact, Actions do provide this functionality, |
We can move this discussion into some place else, AFAICT this thread is not going to be the best place to discuss other async primitive, you're welcomed to open any issue at Action.js repo if you want to. But keep that in mind. Action is not something emulating Promise, it's a different thing at all. What it abstract is not the value :: T, but the continuation :: ( T -> r ) -> r . |
@winterland1989 @spion is well aware of that, that's what he was saying. Let's stop this discussions here - further off topic discussion will be moderated away. |
As far as fragmentation goes: I don't see how adding promises into the core would remedy that for the Node community (or how it is a pro and not a con), but rather make it worse... From my understanding, moving something out of userland and into the core doesn't remove fragmentation, providing a consistent API in the core removes fragmentation (at least on Node's end, there will always be fragmentation in userland). So, with that in mind, it would actually add fragmentation. But then again, what is so bad about fragmentation in this area anyways? (Not rhetorical, actually curious) I think this comment touches on that, at least in userland. But, again, I don't think moving it into the core would solve that, because those userland libraries still provide a promise API, like Node would implement, but with more methods (which causes the fragmentation), and I see no reason why that would just stop and end fragmentation. I use promises quite often, and for a while I was quite excited for them to be added into the core, but after reading the discussions here, I understand the ideology of those who don't want them in the core (which is why I thought maybe I should make a comment), and now, I somewhat agree. Fragmentation is inevitable now because of userland, bloating the core with another option doesn't solve that. I'm happy with using userland promises (and I'd like to know why other promise users aren't too). Of course, fragmentation isn't the only pushing factor, I just thought I would touch on that. (As well as get some answers to what I'm curious about.) I'm not too knowledgeable when it comes to this type of stuff, but I would still like to contribute to the conversation somehow (keep in mind, I'm pretty young, 14, with little experience on discussions like these. I thought participating in this conversation might be a good for learning, and its fun) |
@jamen Thanks for commenting! I think "fragmentation" (as @balupton uses it in the issue text) might be a bit of a loaded term — it may be more accurate to say the API provides a default, "known good" path for promise users to write packages, reduces the need for alternative implementations to maintain shim libraries, and allows users to "opt in" to choosing alternative implementations (vs. the current situation, where the decision is front-loaded.) This PR aims to gradually smooth interoperation between promise-using packages in this fashion — it won't happen all at once, but I believe the combination of providing an API and documenting best practices for interoperation with alternative implementations in core will make the situation for users much better in the long run. With regards to fragmentation between promises and callbacks, at a code level that's not addressed in the current PR, though we can start to bridge that by exposing At a community level, providing APIs for both callback and promise users sends the message that both are equally valid ways to build a program, and preference towards one or the other is equally valid. Promises and callbacks thus far have been treated like a zero-sum game: for promise users to make gains, callback users have to lose, or vice versa. By including the API, core asserts that it isn't a zero sum game — promise users and callback users can both be catered to, without either side losing ground. I believe this would go a long way towards cooling the temperature of the promises vs. callbacks conversation. |
A topic to consolidate information on why the PR is even being considered.
Pro Arguments
Pros of Promises in the Core
Promise.all
and the upcomingawait
Pros of Promises in General
await
is dependent on promisesCon Arguments
Cons of Promises in the Core
Cons of Promises in General
More details.
Feel free to update with additional resources and if you still don't like the PR add to the discussion here.
The text was updated successfully, but these errors were encountered: