KEMBAR78
Jscex: Write Sexy JavaScript | PDF
Jscex
Write Sexy JavaScript
    Zhao Jie - SNDA - May 2011
About Me
•       /       / Jeffrey Zhao /

• Programmer
• Blogger - http://blog.zhaojie.me/
• Twitter: @jeffz_cn
• F#, JavaScript, Scala, C#, Python, .NET, Mono...
• Java (as a language) hater
What’s Jscex

• JavaScript Computation EXpression
• A JavaScript language extension to help
  programming in common scenarios
• A port of F# Computation Expression
  •   Inspired by the idea
  •   Design for JavaScript
What’s NOT Jscex

• Another language
  •   Jscex is 100% JavaScript

• A framework
  •   It’s a library, works with any libraries / frameworks

• A JavaScript engine / runtime
  •   Execute in any ECMAScript 3 engines
Taste a Bit
Bubble Sort
var compare = function (x, y) {
    return x - y;
}

var swap = function (a, i, j) {
    var t = a[x]; a[x] = a[y]; a[y] = t;
}

var bubbleSort = function (array) {
    for (var x = 0; x < array.length; x++) {
        for (var y = 0; y < array.length - x; y++) {
            if (compare(array[y], array[y + 1]) > 0) {
                swap(array, y, y + 1);
            }
        }
    }
}
Animate it!
var compare = function (x, y, callback) {         var innerLoop = function (array, x, y, callback) {
    setTimeout(10, function () {                      if (y < array.length - x) {
        callback(x - y);                                  compare(array[y], array[y + 1], function (r) {
    });                                                        if (r > 0) {
}                                                                  swap(array, y, y + 1, function () {
                                                                        innerLoop(array, x, y + 1, callback);
var swap = function (a, i, j, callback) {                          });
    var t = a[x]; a[x] = a[y]; a[y] = t;                       } else {
    repaint(a);                                                    innerLoop(array, x, y + 1, callback);
                                                               }
    setTimeout(20, callback);                             });
}                                                     } else {
                                                          callback();
var outerLoop = function (array, x, callback) {       }
    if (x < array) {                              }
        innerLoop(array, x, 0, function () {
             outerLoop(array, x + 1, callback);   outerLoop(array, 0, function () {
        });                                           console.log("done!");
    } else {                                      });
        callback();
    }
}
Animate it!
                                                                             at?
var compare = function (x, y, callback) {



                                                                           th
                                                  var innerLoop = function (array, x, y, callback) {




                                                                        is
    setTimeout(10, function () {                      if (y < array.length - x) {
        callback(x - y);                                  compare(array[y], array[y + 1], function (r) {




               ll
    });                                                        if (r > 0) {
}                                                                  swap(array, y, y + 1, function () {




              e
                                                                        innerLoop(array, x, y + 1, callback);




             h
var swap = function (a, i, j, callback) {                          });
    var t = a[x]; a[x] = a[y]; a[y] = t;                       } else {




           e
    repaint(a);                                                    innerLoop(array, x, y + 1, callback);




          h
                                                               }




         t
    setTimeout(20, callback);                             });




       t
}                                                     } else {




      a
                                                          callback();




     h
var outerLoop = function (array, x, callback) {       }
    if (x < array) {                              }
        innerLoop(array, x, 0, function () {




    W
             outerLoop(array, x + 1, callback);   outerLoop(array, 0, function () {
        });                                           console.log("done!");
    } else {                                      });
        callback();
    }
}
Bubble Sort Animation
var compareAsync = eval(Jscex.compile("async", function (x, y) {
     $await(Jscex.Async.sleep(10)); // each "compare" takes 10 ms.
     return x - y;
}));

var swapAsync = eval(Jscex.compile("async", function (a, x, y) {
     var t = a[x]; a[x] = a[y]; a[y] = t; // swap
     repaint(a); // repaint after each swap
     $await(Jscex.Async.sleep(20)); // each "swap" takes 20 ms.
}));

var bubbleSortAsync = eval(Jscex.compile("async", function (array) {
     for (var x = 0; x < array.length; x++) {
         for (var y = 0; y < array.length - x; y++) {
             var r = $await(compareAsync(array[y], array[y + 1]));
             if (r > 0) $await(swapAsync(array, y, y + 1));
         }
     }
}));
Bubble Sort Animation
var compareAsync = eval(Jscex.compile("async", function (x, y) {
     $await(Jscex.Async.sleep(10)); // each "compare" takes 10 ms.
     return x - y;
}));

var swapAsync = eval(Jscex.compile("async", function (a, x, y) {
     var t = a[x]; a[x] = a[y]; a[y] = t; // swap
     repaint(a); // repaint after each swap
     $await(Jscex.Async.sleep(20)); // each "swap" takes 20 ms.
}));

var bubbleSortAsync = eval(Jscex.compile("async", function (array) {
     for (var x = 0; x < array.length; x++) {
         for (var y = 0; y < array.length - x; y++) {
             var r = $await(compareAsync(array[y], array[y + 1]));
             if (r > 0) $await(swapAsync(array, y, y + 1));
         }
     }
}));
Design Principle #1
Keep Everything for
JavaScript Programmer

• Language features
• Language semantics
• Programming experiences
Language Features
• Support almost all JavaScript constructs
  •   Loops: while / for / for...in / do
  •   Conditions: if / switch
  •   Error handling: try...catch...finally
  •   Others: return / break / continue / throw

• Not supported
  •   with block
  •   break / continue to label
  •   conditional break in switch block

• Nested functions
Language Semantics

• Keep all semantics as JavaScript
• Explicit “bind” operations (like $await)
  •   The only extension to the language
  •   Act like a method call
  •   Make clear which operation is “special”
Programming
          Experiences

• Write, execute, debug as JavaScript
• Modify and take effects immediately
• No extra compilation process
 •   Code generation by JIT compiler as code runs
Async Programming is
      Difficult
Code Locality is
           Broken
• Used to expressing algorithms linearly
• Async requires logical division of algorithms
• Very difficult to
  •   Combine multiple asynchronous operations
  •   Deal with exceptions and cancellation
Asynchronous Function
 // use async builder to execute the compiled code
 var somethingAsync = eval(Jscex.compile("async",
     function (...) {
         // implementation
     }
 ));
React!!!
function () {
    var res = $await(<async work>);
}
React!!!
function () {
    var res = $await(<async work>);
}


      an HTTP Request
          an UI Event
       a Timer Callback
      a Query Response
   a Web Service Response
      an Agent Message
function () {
          var img = $await(readAsync("http://..."));
          console.log("loaded!");
          $await(writeAsync("./files/..."));
          console.log("saved!");
      }

                            =
(function () {
    var _b_ = Jscex.builders["async"];
    return _b_.Start(this,
        _b_.Delay(function () {
            _b_.Bind(readAsync(...), function (img) {
                console.log("loaded!");
                return _b_.Bind(writeAsync(...), function () {
                    console.log("saved!");
                    return _b_.Normal();
                });
            });
        })
    );
})
Work with Express
var app = express.createServer();

app.get('/', function (req, res) {
    /**
     * Question: How to do async work here? e.g.:
     *
     * 1. Get multiple keys from database.
     * 2. For each key, try get data from cache.
     *    If cache missed, get data from database.
     * 3. Write a list of result to the response.
     *
     * Note: all "get" operations are asynchronous.
     **/
});

app.listen(3000);
app.getAsync('/', eval(Jscex.compile("async", function (req, res) {

    var keys = $await(db.getKeysAsync(...));

    var results = [];
    for (var i = 0; i < keys.length; i++) {
        var r = $await(cache.getAsync(keys[i]));
        if (!r) {
            r = $await(db.getAsync(keys[i]));
        }

        results.push(r);
    }

    res.send(generateList(results));
})));
I/O Parallelism

• Software is often I/O bound
 •   Leveraging web services
 •   Working with data on disk

• Network and disk speeds increasing slower
• I/O resources are inherently parallel
 •   Huge opportunity for performance
Make Things Parallel
var getDataAsync = eval(Jscex.compile("async", function (key) {
     var res = $await(cache.getAsync(key));
     if (res) return res;
     return $await(db.getAsync(key));
}));

app.getAsync('/', eval(Jscex.compile("async", function (req, res) {

    var keys = $await(db.getKeysAsync(...));

    // get tasks of “get data” (not started yet)
    var tasks = keys.map(function (key) {
        return getDataAsync(key);
    });

    // make requests in parallel
    var results = $await(Jscex.Async.parallel(tasks));

    res.send(generateList(results));
})));
Task Model
• Async library use a simple task model
  •   Easy to write bindings for async operations
  •   Parallel: $await(Jscex.Async.parallel(taskA,   taskB))

  •   Series: $await(taskA.continueWith(taskB))

• The semantics of $await: wait a task to
  complete
  •   Start the task if it’s not running
  •   Return immediately if it’s already completed

• We can start a task manually (if necessary)
  •   E.g. taskA.start();   $await(taskB); $await(taskA);
Advanced
Async Patterns
The Limitation of
Callback-based Model
// if the sequence of processing is important, how to keep it?
var i = 1;
conn.onAsync("data", eval(Jscex.compile("async", function () {
	   var id = i++;

    $await(step1); console.log("step 1 - request " + id);
    $await(step2); console.log("step 2 - request " + id);

    /**
      * A possible sequence (which is unacceptable):
      *     step 1 - request 1
      *     step 1 - request 2
      *     step 2 - request 2
      *     step 2 - request 1
      **/
})));
Erlang-like Agent
var i = 0;
var agent = Agent.start(eval(Jscex.compile("async", function (mailbox) {
    var id = i++;

    var msg = $await(mailbox.receive());
    $await(step1); console.log("step 1 - request " + id);
    $await(step2); console.log("step 2 - request " + id);
})));

conn.on("data", function (data) {
    // data would be queued in mailbox
    agent.send(data);
});
Jscex Components
Language & Libraries
• Compiler (language extension)
  •   JIT: generate code at runtime (development)
  •   AOT: generate code before runtime (production)

• Builders (libraries)
  •   Async: simplfied async programming
  •   Sequense: generator for lazy iterator (Py, C#, JS 1.7)
  •   more...
AOT Compiler
// before AOT compilation
Agent.start(eval(Jscex.compile("async", function (mailbox) {
    ...
})));

// after AOT compilation
// independent of compiler scripts
// need a tiny library only (3kb when gzipped)
Agent.start((function (mailbox) {
     var _b_ = Jscex.builders["async"];
     return _b_.Start(this,
         ...
     );
}));
Jscex Builders

• Compiler generates code into standard
  patterns with a builder (name)
 •   A builder defines its name of the “bind” operation

• Execute with the standard methods
  implemented in the builder
Not only for Async
// infinite fibonacci sequence
var fib = eval(Jscex.compile("seq", function () {
    var i = 0, j = 1;
    while (true) {
        $yield(i); // the bind operation

           var t = i;
           i = j;
           j += t;
       }
}));

var iter = fib().skip(10).take(10);
while (iter.moveNext()) {
    console.log(iter.current);
}
... and Maybe Monad
var maybeFunc = function () {
    var maybeA = getA();
    if (maybeA == Maybe.None) return Maybe.None;

	   var maybeB = getB();
     if (maybeB == Maybe.None) return Maybe.None;

    return maybeA.value + maybeB.value;
}

// simplified version
var maybeFunc = eval(Jscex.compile("maybe", function () {
     var a = $try(getA());
     var b = $try(getB());
     return a + b;
}));
Jscex Internals
Performance
• Function has “linear” and “async” parts
  •   Linear part: compiler keeps linear codes as much as
      possible
  •   Async part: at the same level of hand-writing, callback-
      based implementation

• An async operation always takes much
  more time than a bunch of normal codes
  •   Jscex never becomes a performance issue / bottleneck
Code Generation
        Patterns
• Code generation is direct and simple
 •   Map to original code easily
 •   Easy to debug: use “debugger” statement to pause or
     add breakpoints in debuggers

• Based on standard methods
 •   Start, Delay, Combine
 •   Loop, Try
 •   Normal, Return, Break, Continue, Throw
 •   Bind
Debug Browser Scripts
Debug Node.js Scripts
Try it NOW


• Release under BSD license
• https://github.com/JeffreyZhao/jscex
Q &A
Thanks

Jscex: Write Sexy JavaScript

  • 1.
    Jscex Write Sexy JavaScript Zhao Jie - SNDA - May 2011
  • 2.
    About Me • / / Jeffrey Zhao / • Programmer • Blogger - http://blog.zhaojie.me/ • Twitter: @jeffz_cn • F#, JavaScript, Scala, C#, Python, .NET, Mono... • Java (as a language) hater
  • 3.
    What’s Jscex • JavaScriptComputation EXpression • A JavaScript language extension to help programming in common scenarios • A port of F# Computation Expression • Inspired by the idea • Design for JavaScript
  • 4.
    What’s NOT Jscex •Another language • Jscex is 100% JavaScript • A framework • It’s a library, works with any libraries / frameworks • A JavaScript engine / runtime • Execute in any ECMAScript 3 engines
  • 5.
  • 6.
    Bubble Sort var compare= function (x, y) { return x - y; } var swap = function (a, i, j) { var t = a[x]; a[x] = a[y]; a[y] = t; } var bubbleSort = function (array) { for (var x = 0; x < array.length; x++) { for (var y = 0; y < array.length - x; y++) { if (compare(array[y], array[y + 1]) > 0) { swap(array, y, y + 1); } } } }
  • 7.
    Animate it! var compare= function (x, y, callback) { var innerLoop = function (array, x, y, callback) { setTimeout(10, function () { if (y < array.length - x) { callback(x - y); compare(array[y], array[y + 1], function (r) { }); if (r > 0) { } swap(array, y, y + 1, function () { innerLoop(array, x, y + 1, callback); var swap = function (a, i, j, callback) { }); var t = a[x]; a[x] = a[y]; a[y] = t; } else { repaint(a); innerLoop(array, x, y + 1, callback); } setTimeout(20, callback); }); } } else { callback(); var outerLoop = function (array, x, callback) { } if (x < array) { } innerLoop(array, x, 0, function () { outerLoop(array, x + 1, callback); outerLoop(array, 0, function () { }); console.log("done!"); } else { }); callback(); } }
  • 8.
    Animate it! at? var compare = function (x, y, callback) { th var innerLoop = function (array, x, y, callback) { is setTimeout(10, function () { if (y < array.length - x) { callback(x - y); compare(array[y], array[y + 1], function (r) { ll }); if (r > 0) { } swap(array, y, y + 1, function () { e innerLoop(array, x, y + 1, callback); h var swap = function (a, i, j, callback) { }); var t = a[x]; a[x] = a[y]; a[y] = t; } else { e repaint(a); innerLoop(array, x, y + 1, callback); h } t setTimeout(20, callback); }); t } } else { a callback(); h var outerLoop = function (array, x, callback) { } if (x < array) { } innerLoop(array, x, 0, function () { W outerLoop(array, x + 1, callback); outerLoop(array, 0, function () { }); console.log("done!"); } else { }); callback(); } }
  • 9.
    Bubble Sort Animation varcompareAsync = eval(Jscex.compile("async", function (x, y) { $await(Jscex.Async.sleep(10)); // each "compare" takes 10 ms. return x - y; })); var swapAsync = eval(Jscex.compile("async", function (a, x, y) { var t = a[x]; a[x] = a[y]; a[y] = t; // swap repaint(a); // repaint after each swap $await(Jscex.Async.sleep(20)); // each "swap" takes 20 ms. })); var bubbleSortAsync = eval(Jscex.compile("async", function (array) { for (var x = 0; x < array.length; x++) { for (var y = 0; y < array.length - x; y++) { var r = $await(compareAsync(array[y], array[y + 1])); if (r > 0) $await(swapAsync(array, y, y + 1)); } } }));
  • 10.
    Bubble Sort Animation varcompareAsync = eval(Jscex.compile("async", function (x, y) { $await(Jscex.Async.sleep(10)); // each "compare" takes 10 ms. return x - y; })); var swapAsync = eval(Jscex.compile("async", function (a, x, y) { var t = a[x]; a[x] = a[y]; a[y] = t; // swap repaint(a); // repaint after each swap $await(Jscex.Async.sleep(20)); // each "swap" takes 20 ms. })); var bubbleSortAsync = eval(Jscex.compile("async", function (array) { for (var x = 0; x < array.length; x++) { for (var y = 0; y < array.length - x; y++) { var r = $await(compareAsync(array[y], array[y + 1])); if (r > 0) $await(swapAsync(array, y, y + 1)); } } }));
  • 11.
  • 12.
    Keep Everything for JavaScriptProgrammer • Language features • Language semantics • Programming experiences
  • 13.
    Language Features • Supportalmost all JavaScript constructs • Loops: while / for / for...in / do • Conditions: if / switch • Error handling: try...catch...finally • Others: return / break / continue / throw • Not supported • with block • break / continue to label • conditional break in switch block • Nested functions
  • 14.
    Language Semantics • Keepall semantics as JavaScript • Explicit “bind” operations (like $await) • The only extension to the language • Act like a method call • Make clear which operation is “special”
  • 15.
    Programming Experiences • Write, execute, debug as JavaScript • Modify and take effects immediately • No extra compilation process • Code generation by JIT compiler as code runs
  • 16.
  • 17.
    Code Locality is Broken • Used to expressing algorithms linearly • Async requires logical division of algorithms • Very difficult to • Combine multiple asynchronous operations • Deal with exceptions and cancellation
  • 18.
    Asynchronous Function //use async builder to execute the compiled code var somethingAsync = eval(Jscex.compile("async", function (...) { // implementation } ));
  • 19.
    React!!! function () { var res = $await(<async work>); }
  • 20.
    React!!! function () { var res = $await(<async work>); } an HTTP Request an UI Event a Timer Callback a Query Response a Web Service Response an Agent Message
  • 21.
    function () { var img = $await(readAsync("http://...")); console.log("loaded!"); $await(writeAsync("./files/...")); console.log("saved!"); } = (function () { var _b_ = Jscex.builders["async"]; return _b_.Start(this, _b_.Delay(function () { _b_.Bind(readAsync(...), function (img) { console.log("loaded!"); return _b_.Bind(writeAsync(...), function () { console.log("saved!"); return _b_.Normal(); }); }); }) ); })
  • 22.
    Work with Express varapp = express.createServer(); app.get('/', function (req, res) { /** * Question: How to do async work here? e.g.: * * 1. Get multiple keys from database. * 2. For each key, try get data from cache. * If cache missed, get data from database. * 3. Write a list of result to the response. * * Note: all "get" operations are asynchronous. **/ }); app.listen(3000);
  • 23.
    app.getAsync('/', eval(Jscex.compile("async", function(req, res) { var keys = $await(db.getKeysAsync(...)); var results = []; for (var i = 0; i < keys.length; i++) { var r = $await(cache.getAsync(keys[i])); if (!r) { r = $await(db.getAsync(keys[i])); } results.push(r); } res.send(generateList(results)); })));
  • 24.
    I/O Parallelism • Softwareis often I/O bound • Leveraging web services • Working with data on disk • Network and disk speeds increasing slower • I/O resources are inherently parallel • Huge opportunity for performance
  • 25.
    Make Things Parallel vargetDataAsync = eval(Jscex.compile("async", function (key) { var res = $await(cache.getAsync(key)); if (res) return res; return $await(db.getAsync(key)); })); app.getAsync('/', eval(Jscex.compile("async", function (req, res) { var keys = $await(db.getKeysAsync(...)); // get tasks of “get data” (not started yet) var tasks = keys.map(function (key) { return getDataAsync(key); }); // make requests in parallel var results = $await(Jscex.Async.parallel(tasks)); res.send(generateList(results)); })));
  • 26.
    Task Model • Asynclibrary use a simple task model • Easy to write bindings for async operations • Parallel: $await(Jscex.Async.parallel(taskA, taskB)) • Series: $await(taskA.continueWith(taskB)) • The semantics of $await: wait a task to complete • Start the task if it’s not running • Return immediately if it’s already completed • We can start a task manually (if necessary) • E.g. taskA.start(); $await(taskB); $await(taskA);
  • 27.
  • 28.
    The Limitation of Callback-basedModel // if the sequence of processing is important, how to keep it? var i = 1; conn.onAsync("data", eval(Jscex.compile("async", function () { var id = i++; $await(step1); console.log("step 1 - request " + id); $await(step2); console.log("step 2 - request " + id); /** * A possible sequence (which is unacceptable): * step 1 - request 1 * step 1 - request 2 * step 2 - request 2 * step 2 - request 1 **/ })));
  • 29.
    Erlang-like Agent var i= 0; var agent = Agent.start(eval(Jscex.compile("async", function (mailbox) { var id = i++; var msg = $await(mailbox.receive()); $await(step1); console.log("step 1 - request " + id); $await(step2); console.log("step 2 - request " + id); }))); conn.on("data", function (data) { // data would be queued in mailbox agent.send(data); });
  • 30.
  • 31.
    Language & Libraries •Compiler (language extension) • JIT: generate code at runtime (development) • AOT: generate code before runtime (production) • Builders (libraries) • Async: simplfied async programming • Sequense: generator for lazy iterator (Py, C#, JS 1.7) • more...
  • 32.
    AOT Compiler // beforeAOT compilation Agent.start(eval(Jscex.compile("async", function (mailbox) { ... }))); // after AOT compilation // independent of compiler scripts // need a tiny library only (3kb when gzipped) Agent.start((function (mailbox) { var _b_ = Jscex.builders["async"]; return _b_.Start(this, ... ); }));
  • 33.
    Jscex Builders • Compilergenerates code into standard patterns with a builder (name) • A builder defines its name of the “bind” operation • Execute with the standard methods implemented in the builder
  • 34.
    Not only forAsync // infinite fibonacci sequence var fib = eval(Jscex.compile("seq", function () { var i = 0, j = 1; while (true) { $yield(i); // the bind operation var t = i; i = j; j += t; } })); var iter = fib().skip(10).take(10); while (iter.moveNext()) { console.log(iter.current); }
  • 35.
    ... and MaybeMonad var maybeFunc = function () { var maybeA = getA(); if (maybeA == Maybe.None) return Maybe.None; var maybeB = getB(); if (maybeB == Maybe.None) return Maybe.None; return maybeA.value + maybeB.value; } // simplified version var maybeFunc = eval(Jscex.compile("maybe", function () { var a = $try(getA()); var b = $try(getB()); return a + b; }));
  • 36.
  • 37.
    Performance • Function has“linear” and “async” parts • Linear part: compiler keeps linear codes as much as possible • Async part: at the same level of hand-writing, callback- based implementation • An async operation always takes much more time than a bunch of normal codes • Jscex never becomes a performance issue / bottleneck
  • 38.
    Code Generation Patterns • Code generation is direct and simple • Map to original code easily • Easy to debug: use “debugger” statement to pause or add breakpoints in debuggers • Based on standard methods • Start, Delay, Combine • Loop, Try • Normal, Return, Break, Continue, Throw • Bind
  • 39.
  • 40.
  • 41.
    Try it NOW •Release under BSD license • https://github.com/JeffreyZhao/jscex
  • 42.
  • 43.