here's a quick outline of some of the stuff i'm working on in the next release of (fab). once i have the internals working again, i'll push a branch for folks to play with.
right now, (fab) is mostly async, but relies on sync in one case: apps need to return a listener if they require more information to finish. this is a problem because apps may not know if they need more information until they hear from their upstream apps.
for example, a template app needs to return a function based on a string, but that string may come from an async file or http request, so the app can't reliably respond immediately.
to solve this, the use of return
as a means of returning a meaningful value is deprecated (but can still be used to short-circuit if needed). instead of each app returning a listener, it passes it as the this
context of its response.
so, for example, the fab.tap
app would change from this:
fab.tap = function( fn ) {
return function( app ) {
return function() {
fn();
return app.call( this );
}
}
}
to this:
fab.tap = function( fn ) {
this.call( function( app ) {
this( function() {
fn();
app.call( this );
});
});
};
additionally, each app will be able use its length to specify its arity, so that nested functions are no longer necessary to fully curry an app. this means we can trim the above function to this:
fab.tap = function( fn, app ) {
this( function() {
fn();
app.call( this );
});
};
so not only is this app now fully async, it's less code too.
currently, (fab) apps are expected to send a stream of partial JSGI-style objects:
this({ body: "Hello, world!" })();
this({
status: 200,
headers: { "content-length": 13 },
body: "Hello, world!"
})();
this is wasteful for apps that stream many responses, because they need to create an additional object just to send the most common response: a simple body. since the head of a request is usually only sent in one shot, i'm taking it 'out of band', so that responses now look like this:
this( body, head );
combined with the fact that callback functions are now optionally passed as this
, we can simplify the above as follows:
this( "Hello, world!" );
this(
"Hello, world!",
{ status: 200, headers: { "content-length": 13 } }
);
since in both of the above cases this
is invoked without a context, the downstream app knows that the connection is closed, obviating the need for a terminating empty call.
this has another awesome benefit: apps that create (fab)-compatible apps are themselves (fab)-compatible, so it's (fab) all the way down.
(fab) gets a lot of comparisons to lisp due to its, uh, ambitious use of parentheses. but in reality, it's a lot simpler: an app made using (fab) is just a single list of apps that branch based on their arity. there's no nesting, which is something that makes lisp ugly for many.
optionally using commas instead of parens in examples may make this clearer.
The trimmed down syntax shown with fab.tab seems like it will greatly improve both readability and understandability. I look forward to playing with the new syntax.