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 <10...@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 <10...@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
>> 
>> 

  • ... Marcus Agehall (nu med K-mrkt fastighet och ny elcentral) @ Pike (-) developers forum
    • ... Pontus Östlund
    • ... Martin Karlgren
      • ... Martin Karlgren
      • ... Marcus Agehall (nu med K-mrkt fastighet och ny elcentral) @ Pike (-) developers forum
        • ... Martin Karlgren
    • ... Per Hedbor () @ Pike (-) developers forum
      • ... Pontus Östlund
        • ... Per Hedbor () @ Pike (-) developers forum
          • ... Marcus Comstedt (ACROSS) (Hail Ilpalazzo!) @ Pike (-) developers forum
            • ... Per Hedbor () @ Pike (-) developers forum
              • ... Stephen R. van den Berg
                • ... Stephen R. van den Berg
                • ... Stephen R. van den Berg
        • ... Chris Angelico
    • ... Per Hedbor () @ Pike (-) developers forum

Reply via email to