ifE seems to be broken in Flapjax library 2.1

7 views
Skip to first unread message

sla

unread,
Mar 15, 2010, 6:31:58 PM3/15/10
to Flapjax
HI,
i was trying to do simple function for multiple sinks -
function chooseE (inputE, boolE, outTrueE, outFalseE){
boolE.ifE(inputE.mapE(function(p){return function(f){return
f(true,p)}}) , inputE.mapE(function(p){return function(f){return
f(false,p)}}))
.mapE(function(f) {f(function(truth, val) {if(truth)
outTrueE.sendEvent(val); else outFalseE.sendEvent(val)})});
};


But eventually without any luck, I'm constantly getting errors "Object
Expected" in Flapjax library line 567. Which points to

EventStream.prototype.ifE = function(thenE,elseE) {
var testStamp = -1;
var testValue = false;

createNode([this],function(pulse) { testStamp = pulse.stamp;
testValue = pulse.value; return doNotPropagate; });

return mergeE(createNode([thenE],function(pulse) { if (testValue &&
(testStamp == pulse.stamp)) { send(pulse); } }),
createNode([elseE],function(pulse) { if (!testValue && (testStamp
== pulse.stamp)) { send(pulse); } }));
};


And specifically to: send(pulse)
Function "send" is null.

Am i doing something wrongly?
Maybe there's some known fix already?

Thanks.

Artyom Shalkhakov

unread,
Mar 16, 2010, 10:48:19 AM3/16/10
to fla...@googlegroups.com
Hello,

2010/3/16 sla <sla...@efactorlabs.com>:


> HI,
> i was trying to do simple function for multiple sinks -
> function chooseE (inputE, boolE, outTrueE, outFalseE){
>                boolE.ifE(inputE.mapE(function(p){return function(f){return
> f(true,p)}}) , inputE.mapE(function(p){return function(f){return
> f(false,p)}}))
>                .mapE(function(f) {f(function(truth, val) {if(truth)
> outTrueE.sendEvent(val); else outFalseE.sendEvent(val)})});
>        };

Can you desribe *what* do you try to accomplish? Do you want to get an
event stream which propagates events of one of its input streams,
depending on another event stream? Even if this guess is correct, the
goal is too low-level (what do you need this for?), out-of-context and
thus hard to follow.

> But eventually without any luck, I'm constantly getting errors "Object
> Expected" in Flapjax library line 567. Which points to
>
> EventStream.prototype.ifE = function(thenE,elseE) {
>  var testStamp = -1;
>  var testValue = false;
>
>  createNode([this],function(pulse) { testStamp = pulse.stamp;
> testValue = pulse.value; return doNotPropagate; });
>
>  return mergeE(createNode([thenE],function(pulse) { if (testValue &&
> (testStamp == pulse.stamp)) { send(pulse); } }),
>    createNode([elseE],function(pulse) { if (!testValue && (testStamp
> == pulse.stamp)) { send(pulse); } }));
> };
>
>
> And specifically to: send(pulse)
> Function "send" is null.
>
> Am i doing something wrongly?
> Maybe there's some known fix already?

Perhaps you've encountered a type error.

One unusual aspect of Flapjax is that it has a "typed" API. Did you
notice the funny signatures assigned to Flapjax functions in the
documentation?

Also, you might want to change your mindset: think hard about data
dependencies, not the commands to give.

For example, in an expression like [r = (a + b) * c] r depends on a,
b, and c, and the equal sign should not be taken for assignment!
Rather, [r] is now merely an abbreviation for [(a + b) * c]. So, in
Flapjax you'd write:

> var a_ = ... // initialization, repeated for b_, c_
> var r = liftB(function(a,b,c) {return (a + b) * c;}, a_, b_, c_);

This concept carries to event streams as well.

I hope this clarifies things a bit.

Cheers,
Artyom Shalkhakov

sla

unread,
Mar 16, 2010, 3:13:25 PM3/16/10
to Flapjax

On Mar 16, 10:48 am, Artyom Shalkhakov <artyom.shalkha...@gmail.com>
wrote:
> Hello,
>
> 2010/3/16 sla <sla...@efactorlabs.com>:
>

> Can you desribe *what* do you try to accomplish? Do you want to get an
> event stream which propagates events of one of its input streams,
> depending on another event stream? Even if this guess is correct, the
> goal is too low-level (what do you need this for?), out-of-context and
> thus hard to follow.

I want separate one stream into multiple sinks based on some criteria.
Here the same example through liftB, which is kinda ugly but fast way
around.
liftB(function(truth,val){if(val){if(truth) outTrueE.sendEvent(val);
else outFalseE.sendEvent(val)}},
startsWith(boolE,undefined),startsWith(inputE,undefined));


> > But eventually without any luck, I'm constantly getting errors "Object
> > Expected" in Flapjax library line 567. Which points to
>
> > EventStream.prototype.ifE = function(thenE,elseE) {
> >  var testStamp = -1;
> >  var testValue = false;
>
> >  createNode([this],function(pulse) { testStamp = pulse.stamp;
> > testValue = pulse.value; return doNotPropagate; });
>
> >  return mergeE(createNode([thenE],function(pulse) { if (testValue &&
> > (testStamp == pulse.stamp)) { send(pulse); } }),
> >    createNode([elseE],function(pulse) { if (!testValue && (testStamp
> > == pulse.stamp)) { send(pulse); } }));
> > };
>
> > And specifically to: send(pulse)
> > Function "send" is null.
>
> > Am i doing something wrongly?
> > Maybe there's some known fix already?
>
> Perhaps you've encountered a type error.

Nope, just send function is not defined. if you change it to
sendEvent(thenE, pulse.value) everything would work. But then you
would have to propagate pulse.
I tried a bit to play with it, hoping for easy fix - no luck.
Simple functions like one below works perfectly:
i= extractEventE(document, "mouseMove");
var c= i.mapE(function(){return true});
c.ifE(i.mapE(function(){return 1}), i.mapE(function(p){return 0)});

(syntax might be off, typed right here)


> One unusual aspect of Flapjax is that it has a "typed" API. Did you
> notice the funny signatures assigned to Flapjax functions in the
> documentation?

Well it snot really typed. But you cant check type of resultant
stream. You cant say one stream of type (A->B->C) and another of type
(A->C). You can send any value of any type into stream, and mutate
state or function in any way you want, or create any kind of stream
and to separate between them you would have to pass state somehow with
them.
Don't get me wrong though, i don't think it's an issue of Flapjax.


> Also, you might want to change your mindset: think hard about data
> dependencies, not the commands to give.
>
> For example, in an expression like [r = (a + b) * c] r depends on a,
> b, and c, and the equal sign should not be taken for assignment!
> Rather, [r] is now merely an abbreviation for [(a + b) * c]. So, in
> Flapjax you'd write:
>
> > var a_ = ... // initialization, repeated for b_, c_
> > var r = liftB(function(a,b,c) {return (a + b) * c;}, a_, b_, c_);
>
> This concept carries to event streams as well.
>
> I hope this clarifies things a bit.
>
> Cheers,
> Artyom Shalkhakov

:) Thanks.

Arjun Guha

unread,
Mar 16, 2010, 4:33:47 PM3/16/10
to fla...@googlegroups.com

> I want separate one stream into multiple sinks based on some criteria.
>
> startsWith(boolE,undefined),startsWith(inputE,undefined));

Can you specify these criteria as predicates? For example, if inputE is
a stream of numbers:

var evensE = inputE.filterE(function(n) { return n % 2 === 0; });
var oddsE = inputE.filterE(function(n) { return n % 2 === 1; });

Does that help you?

Arjun

sla

unread,
Mar 16, 2010, 5:43:25 PM3/16/10
to Flapjax


Hi,
Thanks Arjun. Yes its one way of doing it.
I just wanted a nice clean composition :) always later with little
modification could lift it to get boolE and other inputs as parameters
- return function(boolE) {choose(boolE,.... for example.

But sometimes its get's more complex and in case you need to track
state inside of event-stream, let say E(mouseUp, mouseDown, MouseMove)
- track state in a stream and fire bool events accordingly. In this
case separating streams through filterE would require you 2 separate
function and possibly global state synchronization. I just wanted to
create a helper function to avoid it.

No argue you can solve it in many ways, some better for one situation
some are not.


But the main issue if you look at:


EventStream.prototype.ifE = function(thenE,elseE) {
var testStamp = -1;
var testValue = false;

createNode([this],function(pulse) { testStamp = pulse.stamp;
testValue = pulse.value; return doNotPropagate; });

return mergeE(createNode([thenE],function(pulse) { if (testValue &&
(testStamp == pulse.stamp)) { send(pulse); } }),
createNode([elseE],function(pulse) { if (!testValue && (testStamp
== pulse.stamp)) { send(pulse); } }));
};


var ifE = function(test,thenE,elseE) {
if (test instanceof EventStream)
{ return test.ifE(thenE,elseE); }
else
{ return test ? thenE : elseE; }
};


so in case you use "test" as bool - its works naturally.
in case you use test as eventstream, and your pulses in stream are not
synchronized, nothing works.
(so for example ifE(boolE,trueE,falseE) and streams not necessarily
fires simultaneously).
you can get errors form v.stamp being null at line 247 to "send"
function being null.

It seems that "send" function always undefined? I guess it supposed to
be sendEvent?
but then it brings errors in v.stamp. But seems no v.stamp errors in
case streams are synchronized.


Best regards.

Arjun Guha

unread,
Mar 18, 2010, 1:11:25 PM3/18/10
to fla...@googlegroups.com

> state inside of event-stream, let say E(mouseUp, mouseDown, MouseMove)
> - track state in a stream and fire bool events accordingly. In this
>

.collectE is the functional way to tracking state. If state affects
filtering, you probably want to write:

source.collectE().filterE()

But, if filtering is static, use

source.filterE().collectE()

However, I wouldn't get too caught up with collectE. Sometimes, a little
mutation doesn't hurt. In particular:


> function and possibly global state synchronization. I just wanted to
> create a helper function to avoid it.
>

You can avoid global variables and collectE with this pattern:

source.mapE((function() {
var state = ...;
return function () (evt) { ... };})()).

> so in case you use "test" as bool - its works naturally.
> in case you use test as eventstream, and your pulses in stream are not
> synchronized, nothing works.
> (so for example ifE(boolE,trueE,falseE) and streams not necessarily
> fires simultaneously).
> you can get errors form v.stamp being null at line 247 to "send"
> function being null.
>

The higher-level problem with ifE is that we don't use it very much in
the Flapjax applications we write. So, I'm not surprised you found this
bug. I recommend using the subset of Flapjax covered by our OOPSLA
paper. That subset is well understood, throughly tested, and comes with
a suite of examples and design patterns. You're less likely to run into
"research software bugs" with it.

To address your question directly, try rewriting ifE(boolE, trueE,
falseE) as:

boolE.mapE(function (b) { return b ? trueE : falseE; }).switchE()

It isn't quite the same as the ifE solution, but I think you'll find
that it actually works.

Arjun

sla

unread,
Mar 18, 2010, 9:47:24 PM3/18/10
to Flapjax
HI,
thanks for answer.

> .collectE is the functional way to tracking state. If state affects
> filtering, you probably want to write:
>
> source.collectE().filterE()
>
> But, if filtering is static, use
>
> source.filterE().collectE()
>
> However, I wouldn't get too caught up with collectE. Sometimes, a little
> mutation doesn't hurt

Yes, collectE is exactly what I'm using right now. Very helpful
function. Thanks.


> The higher-level problem with ifE is that we don't use it very much in
> the Flapjax applications we write. So, I'm not surprised you found this
> bug. I recommend using the subset of Flapjax covered by our OOPSLA
> paper. That subset is well understood, throughly tested, and comes with
> a  suite of examples and design patterns. You're less likely to run into
> "research software bugs" with it.
> To address your question directly, try rewriting ifE(boolE, trueE,
> falseE) as:
>
> boolE.mapE(function (b) { return b ? trueE : falseE; }).switchE()

That was my first approach, issue is performance of it quite low. (in
case blooE is mutated version of trueE for example). Playing video in
a page and doing some mouse tracking event processing would totally
freeze video (IE8).
liftB version, though works like a charm performance wise.

Thanks, will stick with OOPSLA subset.

Reply all
Reply to author
Forward
0 new messages