There is also the issue of being "future-proof".

Streamline gives me the choice between 3 runtime options: callbacks, 
fibers, generators. Each option has its own advantages and drawbacks: 
callbacks run everywhere, fibers are fast but have memory overhead, 
generators are "pure" JS and will probably get faster over time. The same 
source works with  these 3 runtimes.

But this may not be the end of the game. JS may get a true await keyword or 
some other syntactic sugar tomorrow (fn!(arg)). If this happens and the new 
syntax is highly optimized I can take advantage of it by tweaking the 
pre-processor. I don't have to commit myself to a specific runtime/async 
support library today. I'll be able to switch later. And BTW, this is not 
an academic case, this is what we did with our product when we decided ot 
deploy with the fibers option rather than the callback one.


On Thursday, January 9, 2014 9:24:02 AM UTC+1, Bruno Jouhier wrote:
>
> Of course I can use all sorts of API/functional twists to improve things 
> but:
>
> 1) Code will still be more difficult to read. For example, compare:
>
> function asyncFn4(_) { return asyncFn1(_, a, b).asyncFn2(_, c, d, 
> e).asyncFn3(_, f);  } 
> and:
>
> function asyncFn4(_) { return yield(asyncFn3(yield asyncF2(yield 
> asyncF1(a, b), c, d, e), f);  } 
>
> asyncFn3 and its argument (f) are completely split apart. And this is not 
> even real code: things get worse when arguments are complex parameters 
> containing parentheses (with more risks of parentheses pairing errors).
>
> You can also do:
>
> function asyncFn4(_) { return yield util.chain(["asyncFn1", a, b], 
> ["asyncFn2", c, d, e], ["asyncFn3", f]);
>
> Better. But to me this looks less like JavaScript (where are the 
> functions? ah yes, those string literals!), more like code that a 
> preprocessor would generate (and this is the kind of code that streamline 
> generates in fibers and generators mode). And there is more noise (line 
> gets longer).
>
> 2) Why should we have to twist our APIs to cope with deficiencies of our 
> async tools? In sync-land you have the choice between target.fn(arg) and 
> fn(target, args). Both have a very terse notation and target.fn(arg) just 
> works better in many situations (array.filter(fn1).map(fn2).reduce(fn3) for 
> example). Why should things be different in async-land? The goal of 
> streamline is to allow you to use *all* JS constructs in a natural way, 
> even if you are in async-land. It tries to keep the syntactic overhead as 
> low as possible. Most of the time, the syntactic overhead is reduced to an 
> extra _ parameter, and this is a very useful visual clue because it tells 
> you where your code does yield to the event loop.
>
> 3) What about training overhead? If you have a growing team, you'll need 
> to get newcomers up to speed and give them lots of guidelines (favor 
> composition over chaining, use helper xyz). I'm not saying that streamline 
> has 0 cost but once you know that _ means "wait" (and yield) and !_ means 
> "don't wait" (and return a future on which you'll be able to wait later), 
> you know most of it and you can start to use your existing JS skills to 
> write async code. 
>
> But as I said earlier I probably have an obsession with the signal to 
> noise ratio in code. If you find the alternatives ok (gap between asyncF3 
> and f, util.chain helper), then fine. I'm ready to pay the price of a 
> transparent pre-processing pass to get the leanest code possible.
>  
> On Thursday, January 9, 2014 4:06:52 AM UTC+1, Raynos wrote:
>>
>> your asyncFn4 chaining problem can be solved with functional programming 
>> and composition
>>
>> Rather then using `this` implicitely in chaining format you can pass the 
>> argument in.
>>
>> ```
>> function* asyncFn4(arg) {
>>     // I hope the yield order works like what you would expect here :/
>>     return (yield asyncFn3(yield asyncFn2(yield asyncFn1(arg)));
>> } 
>>
>> // as seperate calls. pretty ugly but linear and not LISPY
>> function* asyncFn5(arg) {
>>     var res = yield asyncFn1(arg);
>>     var res2 = yield asyncFn2(res);
>>     var res3 = yield asyncFn3(res2);
>>     return res3;
>> }
>>
>> // manually compose and call
>> function* asyncFn6(arg) {
>>     return yield* compose([asyncFn1, asyncFn2, asyncFn3])(arg)
>> }
>>
>> // point free style function composition
>> var asyncFn6 = compose([asyncFn1, asyncFn2, asyncFn3])
>>
>> // compose is really simple with yield in for loops.
>> function compose(fns) {
>>     return function* (result) {
>>         for (var i = 0; i < fns.length; i++) {
>>             result = yield fns[i](result);
>>         }
>>         return result
>>     }
>> }
>> ```
>>
>>

-- 
-- 
Job Board: http://jobs.nodejs.org/
Posting guidelines: 
https://github.com/joyent/node/wiki/Mailing-List-Posting-Guidelines
You received this message because you are subscribed to the Google
Groups "nodejs" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to
[email protected]
For more options, visit this group at
http://groups.google.com/group/nodejs?hl=en?hl=en

--- 
You received this message because you are subscribed to the Google Groups 
"nodejs" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
For more options, visit https://groups.google.com/groups/opt_out.

Reply via email to