Something I would recommend, from having written heaps of asynchronous object based systems, with or without remote computers involved, is to treat all references to the promise object going away as an error, and avoid un-needed circular references.
This makes it easy to detect when you accidentally forget the object.
Also, having the object be callable is rather convenient, it can then be put as a normal callback in all the various function based API:s we already have.
From the MiniRPC module, used extensively in the mini codebase:
// This is an object representing a function to be called when // whatever task is being requested has been completed. // // In the RPC case the 'call_id' and 'par' are used to send the return // value, and function_name is for debug purposes. // class ReturnFromCall(protected int call_id, protected object par, protected string function_name) { protected void destroy() { // this should check the done function callback instead.. if( call_id ) { werror("Return was never called for "+function_name+"\n"); if( par ) this.error("Return was never called\n"); } }
protected string _sprintf( int flag, mapping opts ) { // It's rather nice to have a _sprintf for backtraces. But // without a function_name it's harder :) return sprintf("ReturnCall(from %O)", function_name ); }
protected void error(string err) { // .. this is sort of special in the RPC code .. call_id = 0; destruct(this); }
void `()(mixed ... x ) { // Wanted addition #2: Callable promises? call_id = 0; destruct(this); }
mixed try( function x ) //! Call @[x], if the call fails report an error message to //! the remote side. // // syntax of the day: // foo( foo->try() { code to run..; return res;}); // // It might be useful to have a functon that also returns the // value calculated { if( mixed err = catch{return x();}) { // code goes here to log etc.. this.error(err); } } }
Also, I personally belong to the "future/promises don't really add much" camp, but feel free to add the API. :)
(In my opinion it's just another way to complicate asynchronous programming)
24 maj 2016 kl. 17:25 skrev Per Hedbor () @ Pike (-) developers forum 10353@lyskom.lysator.liu.se:
[…] Also, I personally belong to the "future/promises don't really add much" camp, but feel free to add the API. :)
(In my opinion it's just another way to complicate asynchronous programming)
I beg to differ ;)
I think the biggest benefit is that your async programming becomes/or looks more sequential/synchroneous which tend to be easier to follow, or in other words less spaghetti-ish.
But, as Agehall pointed out, for that to work properly in the Pike implementation the on_success/on_failure callbacks should be able to return a new future object that would bubble into an evetual chained on_success/on_failure. As the implementation is right now that’s not possible. But there are benefits as it is implemented now nontheless. In the Protocols.HTTP.Promise module I’ve started working on you can do stuff like:
import Protocols.HTTP;
…
Concurrent.Future f1 = Promise.get_url(url1); Concurrent.Future f2 = Promise.get_url(url2); Concurrent.Future f3 = Promise.get_url(url3);
// Resolve when all is done or one fails. Concurrent.Future all = Concurrent.result(({ f1, f2, f3 });
all->on_success(lambda (array(Promise.Success) res) { // All three request succeeded werror(”URLS: %O\n”, res->url); }) ->on_failure(lambda (Promise.Failure res) { werror(”The URL %O failed\n”, res->url); });
…
Or if you’d want a callback for each individual request
array(Concurrent.Future) each = ({ f1, f2, f3 });
each->on_success(lambda (Promise.Success res) { werror(”%O succeeded with status %O\n”, res->url, res->status); }) ->on_failure(lambda (Promise.Failure res) { werror(”%O failed with status %O\n”, res->url, res->status); });
…
To me it’s quite easy to follow what that code does.
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
I think the biggest benefit is that your async programming becomes/or looks more sequential/synchroneous which tend to be easier to follow, or in other words less spaghetti-ish.
And I definitely beg to differ that.
Take this future-less code from a chat server:
| void rpc_call_muc_join(ReturnCall x, mapping args ) | { | [string muc,string who] = get_args(args, | "muc",stringp, | "inviter",ornull(stringp)); | | Channel channel = get_destination( muc ); | int when = now(); | | channel->when_available() | { | if( !channel->exists() ) | client_error(x, "No such channel" ); | | channel->joined( user->id, when ) { | me->join_channel( channel ) { | channel->add_message( msg( "joined", | "user_ids",({format_destination(me)}), | "by",format_destination(me), | "when", now)) { | x(true); // return true. | }; | }; | }; | }; | }
Todays quiz: Is it really harder to read, and how many asynchronous levels do you think there is in it? :)
Also: Could we get rid of the trailing ';' somehow in the grammar?
Also: Could we get rid of the trailing ';' somehow in the grammar?
You mean have an implicit ; before end braces, like this:
int main() { write("Hello, world!\n"); return 0 }
? Probably possible, but I don't know if it's a good idea...
Well.
I mean the fact that you have to have a ; after } where } define a function in functions (well, that's not really why, but it's where it shows up).
Per Hedbor () @ Pike (-) developers forum wrote:
I mean the fact that you have to have a ; after } where } define a function in functions (well, that's not really why, but it's where it shows up).
Just committed a "fix" for that.
My yacc-fu powers have dwindled a bit though... So if someone would like to check if the proposed solution is: a. sufficient? b. efficient?
I'd be much obliged.
Stephen R. van den Berg wrote:
I mean the fact that you have to have a ; after } where } define a function in functions (well, that's not really why, but it's where it shows up).
Just committed a "fix" for that.
I see that Grubba committed a different fix instead. Maybe you already considered it: but lambda definitions of course are and remain expressions. I hope that this fix doesn't mess that up.
Stephen R. van den Berg wrote:
I mean the fact that you have to have a ; after } where } define a function in functions (well, that's not really why, but it's where it shows up).
Just committed a "fix" for that.
I see that Grubba committed a different fix instead. Maybe you already considered it: but lambda definitions of course are and remain expressions. I hope that this fix doesn't mess that up.
I see that the final fix is in. And I'm pleased to see that at least my original fix wasn't completely off the mark.
Well, I guess, this one was/is for you Per, Rest In Peace.
P.S. is the pike-devel gateway still working in both directions? I get the distinct impression that it's just one-way again.
pike-devel@lists.lysator.liu.se