I have spent some time looking at the new stuff in Concurrent.pmod that Grubba has added to 8.1. Mostly it is code relating to the concept of Promises and Futures which are very popular amongst JavaScript people these days.
I have noted that there seems to be some rather striking differences between how JavaScript treats promises and how Pike does it. I'm not sure this is a good thing(tm) and therefore I'd like to bring this up before 8.1 stabilizes.
In Pike, we have two methods that register callback functions, Future.on_success() and Future.on_failure() as opposed to JS where only one method, then(), is used to register both callbacks. I don't mind having two different methods, but for easier transition between the two languages (i.e. for people writing both client-side and server-side code) having the same API would probably help.
A bigger problem imho is the difference in how promises are actually resolved. In JavaScript, the return value of then() is always a *new* promise which then allows for chaining. In Pike, we return the same promise object. This means that code like
my_promise->on_success(foo)->on_success(bar)
in Pike would result only in a call to bar() once my_promise is resolved whereas in JavaScript, foo() would be called and it's return value would be the input to bar in a new promise.
The whole creation of promises is also somewhat awkward in Pike. In JS, you simply pass a callback that will do all the work and that will recieve a success callback and a failure callback as arguments and off you go. In Pike, I'm not sure what the best way to do it is.
I really like the idea of having promises in Pike, but would it not be much better if we tried to implement an API more similar to what the JS folks are used to? At least I think so...
Here are three links to good resources I've found regarding promises in JS. I'm sure there are a ton of others as well.
https://promisesaplus.com/ http://www.mattgreer.org/articles/promises-in-wicked-detail/ http://bluebirdjs.com/docs/api-reference.html
22 maj 2016 kl. 16:40 skrev Marcus Agehall (nu med K-märkt fastighet och ny elcentral) @ Pike (-) developers forum 10353@lyskom.lysator.liu.se:
I have spent some time looking at the new stuff in Concurrent.pmod that Grubba has added to 8.1. Mostly it is code relating to the concept of Promises and Futures which are very popular amongst JavaScript people these days.
[…]
A bigger problem imho is the difference in how promises are actually resolved. In JavaScript, the return value of then() is always a *new* promise which then allows for chaining. In Pike, we return the same promise object. This means that code like
my_promise->on_success(foo)->on_success(bar)
in Pike would result only in a call to bar() once my_promise is resolved whereas in JavaScript, foo() would be called and it's return value would be the input to bar in a new promise.
I agree that a new promise (or future rather) should be returned. You can blame me since on_success/on_failure were void functions originally so I added the return stuff so you could chain an on_failure onto an on_success. But you are correct, that should probably be a new future, and if on_success/on_failure returns a future that’s the one to be resolved in the next chained on_success/on_failure.
Regards ----------------------------- Pontus Östlund Developer • Roxen AB +46 70-662 81 69
www.roxen.com http://www.roxen.com/ | twitter.com/roxen https://twitter.com/roxen
Pontus ??stlund wrote:
22 maj 2016 kl. 16:40 skrev Marcus Agehall (nu med K-m??rkt fastighet och ny elcentral) @ Pike (-) developers forum 10353@lyskom.lysator.liu.se: A bigger problem imho is the difference in how promises are actually resolved. In JavaScript, the return value of then() is always a *new* promise which then allows for chaining. In Pike, we return the same promise object. This means that code like
my_promise->on_success(foo)->on_success(bar)
in Pike would result only in a call to bar() once my_promise is resolved whereas in JavaScript, foo() would be called and it's return value would be the input to bar in a new promise.
I agree that a new promise (or future rather) should be returned. You can blame me since on_success/on_failure were void functions originally so I added the return stuff so you could chain an on_failure onto an on_success. But you are correct, that should probably be a new future, and if on_success/on_failure returns a future that???s the one to be resolved in the next chained on_success/on_failure.
It seems that this has not been fixed yet. I'm trying to build a SOAP interface using Promises. It is messy at best.
Do we agree that this needs to be fixed? Any objections if I try to fix it?
Stephen R. van den Berg wrote:
Pontus ??stlund wrote:
22 maj 2016 kl. 16:40 skrev Marcus Agehall (nu med K-m??rkt fastighet och ny elcentral) @ Pike (-) developers forum 10353@lyskom.lysator.liu.se: A bigger problem imho is the difference in how promises are actually resolved. In JavaScript, the return value of then() is always a *new* promise which then allows for chaining. In Pike, we return the same promise object. This means that code like
I agree that a new promise (or future rather) should be returned.
It seems that this has not been fixed yet. I'm trying to build a SOAP interface using Promises. It is messy at best.
Do we agree that this needs to be fixed? Any objections if I try to fix it?
While inspecting the code, I find a larger difference between the JavaScript then() API and the current on_success() with regard to setting the result value using return, instead of the success() or failure() method.
So, instead of changing the existing API, I decided to add the JavaScript then() API (which I find easier to work with when chaining promises/futures).
So, instead of changing the existing API, I decided to add the JavaScript then() API (which I find easier to work with when chaining promises/futures).
Because I cannot read the messages here, this is a one-way street for now. Code committed. Comments through email please, not lyskom.
I included some doc fixes at no extra charge.
Incidentally, what is the rationale behind guaranteeing running the success/failure callbacks from the backend thread? Is that to contain any possible exceptions so they don't bleed from the on_success/on_failure methods? Or is it because of order of execution and/or fairness?
I find the following code in Concurrent.pmod:
void success(mixed value) { if (state) error("Promise has already been finalized.\n"); object key = mux->lock(); if (state) error("Promise has already been finalized.\n"); unlocked_success(value); key = 0; }
Now, I realise that the first error() will trigger without acquiring the lock. Is this done because you want to catch coding errors *inside* Concurrent.pmod where you acquire the lock first, then try to finalise again? If so, I'd almost say that the first "if" should be turned into an assert-kind of check which should be skipped in production code.
Regarding chaining – I think you’re looking for Future.map and Future.flat_map. Calling map in a Future will return a new Future that will be fulfilled with the result of the callback provided to map. flat_map allows the callback to return a new Future, which will be chained with the Future initially returned by flat_map.
Future f = do_async_stuff(); f->flat_map (lambda (string result) { return do_more_async_stuff_returning_a_future(result); })->on_success (lambda (string final_result) { }); f->map (lambda (string result) { return reverse (result); })->on_success (lambda (string final_result) { });
Separating it to separate methods is good when you care about typing, but JS doesn’t care too much about typing so they combined everything into “then” in the ES6 Promise implementation (this is my personal reflection, I might be wrong ;) )
/Marty
On 22 May 2016, at 16:40 , Marcus Agehall (nu med K-märkt fastighet och ny elcentral) @ Pike (-) developers forum 10353@lyskom.lysator.liu.se wrote:
I have spent some time looking at the new stuff in Concurrent.pmod that Grubba has added to 8.1. Mostly it is code relating to the concept of Promises and Futures which are very popular amongst JavaScript people these days.
I have noted that there seems to be some rather striking differences between how JavaScript treats promises and how Pike does it. I'm not sure this is a good thing(tm) and therefore I'd like to bring this up before 8.1 stabilizes.
In Pike, we have two methods that register callback functions, Future.on_success() and Future.on_failure() as opposed to JS where only one method, then(), is used to register both callbacks. I don't mind having two different methods, but for easier transition between the two languages (i.e. for people writing both client-side and server-side code) having the same API would probably help.
A bigger problem imho is the difference in how promises are actually resolved. In JavaScript, the return value of then() is always a *new* promise which then allows for chaining. In Pike, we return the same promise object. This means that code like
my_promise->on_success(foo)->on_success(bar)
in Pike would result only in a call to bar() once my_promise is resolved whereas in JavaScript, foo() would be called and it's return value would be the input to bar in a new promise.
The whole creation of promises is also somewhat awkward in Pike. In JS, you simply pass a callback that will do all the work and that will recieve a success callback and a failure callback as arguments and off you go. In Pike, I'm not sure what the best way to do it is.
I really like the idea of having promises in Pike, but would it not be much better if we tried to implement an API more similar to what the JS folks are used to? At least I think so...
Here are three links to good resources I've found regarding promises in JS. I'm sure there are a ton of others as well.
https://promisesaplus.com/ http://www.mattgreer.org/articles/promises-in-wicked-detail/ http://bluebirdjs.com/docs/api-reference.html
25 maj 2016 kl. 09:05 skrev Martin Karlgren marty@roxen.com:
Future f = do_async_stuff(); f->flat_map (lambda (string result) { return do_more_async_stuff_returning_a_future(result); })->on_success (lambda (string final_result) { }); f->map (lambda (string result) { return reverse (result); })->on_success (lambda (string final_result) { });
Sorry, that's not too clear – the third line has nothing to do with the second line. It's a separate example.
/Marty
From looking at the module, that was far from obvious if you ask
me. Even though the docs says just that.
I'm not sure I see how having separate methods here will give us better typing, but maybe it does.
My opinion is that we should try to have an API that is as similar as possible to others and if that isn't possible or desirable, we should provide examples on how to use our API as part of the docs.
Regarding chaining â I think youâre looking for Future.map and Future.flat_map. Calling map in a Future will return a new Future that will be fulfilled with the result of the callback provided to map. flat_map allows the callback to return a new Future, which will be chained with the Future initially returned by flat_map.
Future f = do_async_stuff(); f->flat_map (lambda (string result) { return do_more_async_stuff_returning_a_future(result); })->on_success (lambda (string final_result) { }); f->map (lambda (string result) { return reverse (result); })->on_success (lambda (string final_result) { });
Separating it to separate methods is good when you care about typing, but JS doesnât care too much about typing so they combined everything into âthenâ in the ES6 Promise implementation (this is my personal reflection, I might be wrong ;) )
/Marty
On 22 May 2016, at 16:40 , Marcus Agehall (nu med K-märkt fastighet och ny elcentral) @ Pike (-) developers forum 10353@lyskom.lysator.liu.se wrote:
I have spent some time looking at the new stuff in Concurrent.pmod that Grubba has added to 8.1. Mostly it is code relating to the concept of Promises and Futures which are very popular amongst JavaScript people these days.
I have noted that there seems to be some rather striking differences between how JavaScript treats promises and how Pike does it. I'm not sure this is a good thing(tm) and therefore I'd like to bring this up before 8.1 stabilizes.
In Pike, we have two methods that register callback functions, Future.on_success() and Future.on_failure() as opposed to JS where only one method, then(), is used to register both callbacks. I don't mind having two different methods, but for easier transition between the two languages (i.e. for people writing both client-side and server-side code) having the same API would probably help.
A bigger problem imho is the difference in how promises are actually resolved. In JavaScript, the return value of then() is always a *new* promise which then allows for chaining. In Pike, we return the same promise object. This means that code like
my_promise->on_success(foo)->on_success(bar)
in Pike would result only in a call to bar() once my_promise is resolved whereas in JavaScript, foo() would be called and it's return value would be the input to bar in a new promise.
The whole creation of promises is also somewhat awkward in Pike. In JS, you simply pass a callback that will do all the work and that will recieve a success callback and a failure callback as arguments and off you go. In Pike, I'm not sure what the best way to do it is.
I really like the idea of having promises in Pike, but would it not be much better if we tried to implement an API more similar to what the JS folks are used to? At least I think so...
Here are three links to good resources I've found regarding promises in JS. I'm sure there are a ton of others as well.
https://promisesaplus.com/ http://www.mattgreer.org/articles/promises-in-wicked-detail/ http://bluebirdjs.com/docs/api-reference.html
Below is the example I envisioned when Pontus revealed the Promise-based HTTP stuff. It will fetch all images referenced by a web page, nice and neat. The good thing about futures is that you’ll either get success or fail in the end, nothing in-between (if implemented correctly) (yeah, I know that you already like it, so I’m not really educating you.. :) )
Regarding “then” – maybe we can do it “The Pike Way” and add such a method too... It shouldn’t really conflict with the other stuff.
int main(int argc, array argv) { string base_url = "http://www.cnn.com/"; Standards.URI base_uri = Standards.URI (base_url);
Concurrent.Future img_futures = Protocols.HTTP.Promise.get_url (base_url) ->flat_map (lambda (Protocols.HTTP.Promise.Success res) // flat_map maps the result of the Future returned by // get_url (.Success) to a new Future. { array(string) img_urls = ({}); Parser.HTML p = Parser.HTML(); p->add_tag ("img", lambda (Parser.HTML p, mapping args) { if (string src = args["src"]) { if (has_prefix (src, "http")) { img_urls += ({ Standards.URI (src, base_uri) }); } } }); p->finish (res->data)->read();
// img_futures will contain a Future for each URL in // img_urls (since that's what // Protocols.HTTP.Promise.get_url returns). array(Concurrent.Future) img_futures = map (img_urls, Protocols.HTTP.Promise.get_url);
// Concurrent.results wraps an array of Futures in a // new Future. When all the wrapped Futures have // succeeeded, the new future will succeed with an // array of the wrapped values as its result. return Concurrent.results (img_futures); }) ->map (lambda (array(Protocols.HTTP.Promise.Success) results) // map simply maps the result of the Future to a plain // value (no new Future.) We'll get an array of // Protocols.HTTP.Promise.Success objects. { return results->data; });
img_futures->on_success (lambda(array(string) img_data) // img_data is an array of image data strings. { werror ("Got a bunch of data: %O\n", map (img_data, sizeof)); }); img_futures->on_failure (lambda (mixed err) { werror ("Failed: %O\n", err); }); return -1; }
On 25 May 2016, at 14:10 , Marcus Agehall (nu med K-märkt fastighet och ny elcentral) @ Pike (-) developers forum 10353@lyskom.lysator.liu.se wrote:
From looking at the module, that was far from obvious if you ask me. Even though the docs says just that.
I'm not sure I see how having separate methods here will give us better typing, but maybe it does.
My opinion is that we should try to have an API that is as similar as possible to others and if that isn't possible or desirable, we should provide examples on how to use our API as part of the docs.
Regarding chaining â I think youâre looking for Future.map and Future.flat_map. Calling map in a Future will return a new Future that will be fulfilled with the result of the callback provided to map. flat_map allows the callback to return a new Future, which will be chained with the Future initially returned by flat_map.
Future f = do_async_stuff(); f->flat_map (lambda (string result) { return do_more_async_stuff_returning_a_future(result); })->on_success (lambda (string final_result) { }); f->map (lambda (string result) { return reverse (result); })->on_success (lambda (string final_result) { });
Separating it to separate methods is good when you care about typing, but JS doesnât care too much about typing so they combined everything into âthenâ in the ES6 Promise implementation (this is my personal reflection, I might be wrong ;) )
/Marty
On 22 May 2016, at 16:40 , Marcus Agehall (nu med K-märkt fastighet och ny elcentral) @ Pike (-) developers forum 10353@lyskom.lysator.liu.se wrote:
I have spent some time looking at the new stuff in Concurrent.pmod that Grubba has added to 8.1. Mostly it is code relating to the concept of Promises and Futures which are very popular amongst JavaScript people these days.
I have noted that there seems to be some rather striking differences between how JavaScript treats promises and how Pike does it. I'm not sure this is a good thing(tm) and therefore I'd like to bring this up before 8.1 stabilizes.
In Pike, we have two methods that register callback functions, Future.on_success() and Future.on_failure() as opposed to JS where only one method, then(), is used to register both callbacks. I don't mind having two different methods, but for easier transition between the two languages (i.e. for people writing both client-side and server-side code) having the same API would probably help.
A bigger problem imho is the difference in how promises are actually resolved. In JavaScript, the return value of then() is always a *new* promise which then allows for chaining. In Pike, we return the same promise object. This means that code like
my_promise->on_success(foo)->on_success(bar)
in Pike would result only in a call to bar() once my_promise is resolved whereas in JavaScript, foo() would be called and it's return value would be the input to bar in a new promise.
The whole creation of promises is also somewhat awkward in Pike. In JS, you simply pass a callback that will do all the work and that will recieve a success callback and a failure callback as arguments and off you go. In Pike, I'm not sure what the best way to do it is.
I really like the idea of having promises in Pike, but would it not be much better if we tried to implement an API more similar to what the JS folks are used to? At least I think so...
Here are three links to good resources I've found regarding promises in JS. I'm sure there are a ton of others as well.
https://promisesaplus.com/ http://www.mattgreer.org/articles/promises-in-wicked-detail/ http://bluebirdjs.com/docs/api-reference.html
pike-devel@lists.lysator.liu.se