Syntax

3 views
Skip to first unread message

Julian Morrison

unread,
Oct 24, 2006, 5:33:21 AM10/24/06
to Flapjax
I'm curious, what is the formal syntax of the compilable language? Or
perhaps more usefully, how exactly does it extend JS's syntax?

Michael Greenberg

unread,
Oct 24, 2006, 4:51:53 PM10/24/06
to Flapjax
On Oct 24, 5:33 am, "Julian Morrison" <julian.morri...@gmail.com>
wrote:

> I'm curious, what is the formal syntax of the compilable language? Or
> perhaps more usefully, how exactly does it extend JS's syntax?

The syntax of anything inside a script tag of type "text/flapjax" is
identical to JavaScript. The difference is in semantics --
time-varying expressions are automatically 'lifted' in the compiled
language, Flapjax, while pure clients of the Flapjax Client API must
lift their own expressions.

For example (and I don't know how Google Groups will deal with this, so
bear with me):

<script type="text/flapjax">
var timer = timer_b(100); // timer that changes value once every 100
milliseconds
var currentSeconds = Math.floor(timer / 10); // seconds since midnight,
January 1st, 1970 (oh, what a night!)
</script>

Is rendered in pure JavaScript as:

<script type="text/javascript">
var timer = timer_b(100);
var currentSeconds = timer.transform(function (v) { return Math.floor(v
/ 10); });
</script>

Naturally, the former is preferable.

The compiler will generate compile-time syntax errors, which is not
normal for JavaScript, but these errors would have occurred at
run-time. Additionally, the compiler generates warnings for using
certain syntaxes in the Flapjax language. For example "currentSeconds
+= 5" doesn't necessarily make sense when currentSeconds keeps
changing. (For example, it doesn't make a lot of sense inside of an if
statement.)

Getting back to your question, the only compiler specific syntax is the
templates: curly-bang -- {!, !} and triple-stick -- |||. But these are
parsed out of HTML PCDATA and attribute nodes, so a JavaScript/Flapjax
programmer needn't consider them. We introduced the syntax as a way to
reduce the clutter of "insertDomB" and "insertValueB" calls. For
example, the template language is:

<div>It's been {! currentSeconds ||| (loading...) !} seconds since the
beginning of 1970, and it's all been downhill.</div>

while we must write the following without it:

<script lang="text/flapjax"> // or text/javascript -- it doesn't matter
here
insertDomB(currentSeconds, 'currentSeconds', 'over'); // the last
argument, 'over', isn't technically necessary
</script>
<div>It's been <span id="currentSeconds">(loading...)</span> seconds
... .</div>

This can become a huge mess, so the templates simplify it. We wouldn't
want to encourage doing all of your computation in the display layer!
I'd compare it to Smarty <http://smarty.php.net/> or JSP -- while you
can include complex computations in the tags, it's meant only to ease
the interface between models in code and presentations on screen.

HTH,
Michael

falcon

unread,
Oct 25, 2006, 11:47:35 PM10/25/06
to Flapjax

> For example (and I don't know how Google Groups will deal with this, so
> bear with me):
>
> <script type="text/flapjax">
> var timer = timer_b(100); // timer that changes value once every 100
> milliseconds
> var currentSeconds = Math.floor(timer / 10); // seconds since midnight,
> January 1st, 1970 (oh, what a night!)
> </script>
>
> Is rendered in pure JavaScript as:
>
> <script type="text/javascript">
> var timer = timer_b(100);
> var currentSeconds = timer.transform(function (v) { return Math.floor(v
> / 10); });
> </script>
>
> Michael

This is a very instructive example, perhaps you could post some more
before/after code; preferebly with longer dependency graphs
(floor(timer/10))*someothertimer/firsttimer-10, etc.

Michael Greenberg

unread,
Oct 26, 2006, 1:31:26 AM10/26/06
to Flapjax
On Oct 25, 11:47 pm, "falcon" <shahb...@gmail.com> wrote:
> This is a very instructive example, perhaps you could post some more
> before/after code; preferebly with longer dependency graphs
> (floor(timer/10))*someothertimer/firsttimer-10, etc.

I assume that you're interested in the lifting procedure, viz. the
compilation that makes Flapjax different from JavaScript with the
Flapjax client library. Nothing speaks more clearly than an example.
Given the code:

<html>
<head>
<title>Hello world!</title>
<script type='text/flapjax'>
var timerB = timer_b(100);
var leftB = mouseLeft_b(document);
</script>
</head>
<body>
{! (Math.floor(timerB/1000) % 10) * leftB !}
</body>
</html>

one can just as well write in plain JavaScript with the Flapjax client
library as follows:

<html>
<head>
<title>Hello world!</title>
<script type="text/javascript"
src="/some/path/to/flapjax.js"></script><!-- use
src="/demos/flapjax.js" on our servers !-->
<script type="text/javascript">
function loader() {
flapjaxInit();

var timerB = timer_b(100);
var leftB = mouseLeft_b(document);

var computation = lift_b(function (timer, left) {
return (Math.floor(timer/1000) % 10) * left;
}, timerB, leftB);

insertDomB(computation, 'computation', 'over');
}
</script>
</head>
<body onload="loader()">
<span id="computation" />
</body>
</html>

The real meat of difference here is that we had to manually say
"lift_b". This hand-tuned code will in fact be (slightly,
unnoticeably) faster than the compiled version -- you can see this if
you look at the compiled code. I'll give it just for the computation:

$fx.util.compiler.mixedInsertDom_eb($fx.util.compiler.mixedLift_eb($fxc,
(function(__arg1, __arg2) {
return __arg1 * __arg2;
}), ($fx.util.compiler.mixedLift_eb($fxc, (function(__arg1, __arg2) {
return __arg1 % __arg2;
}), $fx.util.compiler.mixedCall_eb($fxc,
$fx.util.compiler.mixedLift_eb($fxc, (function(__arg1) {
return __arg1.floor;
}), Math), $fx.util.compiler.mixedLift_eb($fxc, (function(__arg1,
__arg2) {
return __arg1 / __arg2;
}), timerB, 1000)), 10)), leftB), "flapjaxsuid0", "over");

If you squint a little when you look at it, you can see that each
primitive (e.g. *, .floor, /) has been "lifted" into a call to lift.
(Here, the call is to mixedLift_eb, which will perform type dispatching
to handle event streams and behaviors appropriately.) One way to think
about how the lifting works is that the function provided is a callback
for values from the behavior or the event stream provided.

Returning to my point, the compiled code will be slightly slower, since
it can't (necessarily) discover what primitive operations can be
grouped together at compile time. But the trade-off is generally worth
it (I think), since the compiled version is much shorter and clearer.
The whole reason Flapjax exists, in fact, is to get rid of callbacks.

Any specific points on lifting I can address?

Michael

falcon

unread,
Oct 27, 2006, 1:10:42 PM10/27/06
to Flapjax
MIchael,
Let me see if I understand how functions are 'lifted' in FRP.
If I have the following statement: "print 1 + seconds" where seconds is
a time dependent value,
then "+" and "print" actually have to be lifted functions. Following
is the definition of lift:
lift2 = function(func, param1, param2){ return f(param1.getValue(),
param2.getValue());}
liftedplus = lift2('+', x,y)
liftedprint = lift2('print',x,y)

param1 and param2 also have to be special wrappers around primitive or
time dependent values, the getValue method returns the most recent
value. For primitives, it always returns the same value, for time
dependent value, it returns the most recent. Each time a time
dependent value is updated, the lifted closure is executed. I'm not
sure how that call back is set up through....

Hm...when I started writing this message, I thought I had a better
understanding than apparently I actually do :) Also, please excuse the
made up/mixed syntax, I'm thinking of Haskell/OCaml as much as
Javascript. Please let me know if you see any obvious or glaring
mistakes.

Michael Greenberg

unread,
Nov 1, 2006, 2:15:05 PM11/1/06
to Flapjax
On Oct 27, 12:10 pm, "falcon" <shahb...@gmail.com> wrote:
> Let me see if I understand how functions are 'lifted' in FRP.
> If I have the following statement: "print 1 + seconds" where seconds is
> a time dependent value, then "+" and "print" actually have to be lifted
> functions.

A function call or the application of an operator can be lifted, but
there's no such thing as a "lifted" function. (Don't be confused by
the "unlifted" operator -- it's just a way of marking a function so
that its applications do not get lifted!)

>Following is the definition of lift:
>
> lift2 = function(func, param1, param2){ return f(param1.getValue(),
> param2.getValue());}

This is missing half of what you need, as you notice:

> [snip]


> For primitives, it always returns the same value, for time dependent
> value, it returns the most recent. Each time a time dependent value is
> updated, the lifted closure is executed. I'm not sure how that call back
> is set up through....

Right. The crux of the lifting is what takes place inside of
mixedLift_eb. If you look at flapjax.js (you can now download a copy
at <http://www.flapjax-lang.org/download> or just read the one in the
demos directory at <http://www.flapjax-lang.org/demos/flapjax.js>,
you'll see that mixedLift_eb (line 4634) simply dispatches to the
client-library function lift_b. This client function lift_b will do
what your lift2 is intended to do. You can look at its source code on
line 2452.

The first block of code turns constants into behaviors, as necessary.
Then the function is made into a behavior, also. Next, a new "node" is
created, which is an event stream showing changes to the results.
Let's look at this line by line.

2470 var res_e =
2471 e.createNode(

This makes a new node in the callback dependency graph.

2472 maybeKeyNode,

This is the context for the node. Don't worry about this -- you'll
notice we just thread through the context given to lift_b.

2473 l.map(be.changes, behaves),

be.changes is the function Behavior.changes. behaves is our list of
arguments to our functions; here, we map the method 'changes' over
them, getting a list of event streams. The argument we're passing here
are the nodes in the dependency graph based on which this node will
update. That is to say, the result of the function application is
updated every time one of its arguments is updated.

2474 function (s, p) {

Here we give the callback to call when one of the above nodes updates.
Flapjax has to use callbacks _somewhere_, and this is where. s is the
callback further up the graph, and p is the event ("pulse") which
triggered this callback.

2475 var vals = l.map(be.valueNow, behaves);

Here we do the equivalent of your getValue method -- we get the current
value of each of the arguents.

2476 s(new b.Pulse(

We'll pass a new pulse on to the node in the graph above this node.

2477 p.stamp,
2478 p.path,
2479 p.accuracy,

These are just some properties of the pulse. Nothing special.

2480 be.valueNow(fnB).apply(context,
vals)));

This is the final property, value. The value of the pulse we'll send
is the result of applying the function to the current value of the
arguments. Voila!

2481 });

We've now created the event stream of new return values of applying the
function. But lift_b wants a behavior, so we need to turn it into a
behavior. That's easy enough.

2482
2483 return new be.Behaviour(

Create a new behavior...

2484 maybeKeyNode,

...with the threaded context...

2485 res_e,

...where the change stream is the event stream of new return values...

2486 be.valueNow(fnB).apply(context, l.map(be.valueNow,
behaves)));

...and the current value is the result of applying the current function
to the current values of its arguments.

So, what have we learned from this? The secret sauce is the callback
dependency graph. My explanation almost certainly isn't clear enough;
Leo, the main author of the client library, can probably give a more
detailed explanation; I can certainly answer any specific question you
have.

HTH,
Mike

Reply all
Reply to author
Forward
0 new messages