Question about timerE (event streams in flapjax)

1 view
Skip to first unread message

pointer

unread,
May 18, 2009, 12:29:04 PM5/18/09
to Flapjax
Hi there,

I know timerE returns an event stream that fires an event at specific
intervals. But I am a bit lost in how to make my desired function to
work. for example, I want to build a sample that reduces from 100 to
0:

// i have a timer now that triggers event every second
var timer = timerE(1000);

// what i want to here is reduce the value of element 'fall' by 1
every sec (timer triggers)
var content = timer.mapE(function(x){
return $B('fall') - 1;
});

// given an init value 100 and fill it in element 'fall'
insertDomB(content.startsWith("100"), 'fall');


I cannot get it working. Maybe this sounds very simple for some you
guys but I tried many ways. What I want to do is just {get a value
from form input element id 'fall', minus 1 then return, replace page
element with a behavior}. According to flapjax api doc, timerE event
value is current system time which is not what I need. I just need my
own function triggers every 1 second. Perhaps I dont understand event
streams in flapjax very well, any advice would be appreciated.

best,

pointer

Dan Bjorge

unread,
May 18, 2009, 3:14:38 PM5/18/09
to Flapjax
Is there a particular reason you want it to use the form element,
rather than the event stream itself, to store your "fall" value? The
following flapjax-compiler-ready code does a slightly simpler version
of what you're asking:


<html>
<head>
<title>Flapjax Demo: Where is the Mouse?</title>
<link rel="stylesheet" href="/demo.css"/>
<script type="text/flapjax">

// Count the number of times we catch up.
var fallB =
timerE(1000). //once per second
collectE(100, //collecting starting at 100
function (_,p) { return p - 1;}). //every time the event occurs,
subtract 1 from the event value p
startsWith(100); //before any events have been fired, the
behavior is 100

</script>
</head>

<body>
<div>Fall = {! fallB.toString() !}</div>
</body>

</html>


-Dan

pointer

unread,
May 20, 2009, 1:09:12 PM5/20/09
to Flapjax
Thanks a lot for help. It greatly helps me rethinking and resolve my
problem.

Now i'm thinking about some other issues. According to flapjax,
traditional callbacks are avoided at most effort. Say, i have a small
div block on page and i want to move its position by keyboard strokes,
arrow directions. Now that I'm not encouraged to use callbacks, i have
to find a way to identify what key user pressed. I have been reading
tutorial and api doc but cannot find an effective way yet.

all the best,

/p

Artyom Shalkhakov

unread,
May 21, 2009, 12:15:40 AM5/21/09
to fla...@googlegroups.com
Hi,

2009/5/20 pointer <che...@gmail.com>:


> Now i'm thinking about some other issues. According to flapjax,
> traditional callbacks are avoided at most effort. Say, i have a small
> div block on page and i want to move its position by keyboard strokes,
> arrow directions. Now that I'm not encouraged to use callbacks, i have
> to find a way to identify what key user pressed. I have been reading
> tutorial and api doc but cannot find an effective way yet.

The key here is to think in terms of data dependencies, I think. What
events/behaviors does a position depend on?

Let's take a small counter example (pun intended). Say you have an
output field that displays current counter value, and a button to increase
that value. That button, when pressed, "creates" an event, and thus
generates a stream of events; every event is an object that holds
a function to increase it's argument by one:

b = function(x) { return x + 1; }, function(x) { return x + 1; }, ...
(I use this representation when I think of event streams [1])

How do we define a counter then? Quite easily: a counter is a *left fold*
over stream of button events (streams are somewhat analogous to singly
linked lists):

a = startsWith(collectE(b, 0, function(x,y) { return x(y); }), 0);

We turned 'a' into a behavior, so it always has a value. Initially, the value is
zero (it's called a 'seed'), but when an event arrives, we apply it to the seed
value, which gives us next seed. The process continues on.

There's more here. We can add another button, and it's event stream would
be defined as follows:

c = function(x) { return x - 1; }, ...

Since no user can press two buttons simultaneously, I think it's safe to combine
two event streams using mergeE:

x = mergeE(b,c)

We can supply this value to the collectE function, creating a counter with two
buttons. :) We can go further, by defining another button:

d = function(_) { return 0; }, ...

Guess what it does. :)

Hope this helps,
Artyom Shalkhakov.

[1] in Flapjax, that would be
clicksE('my-button').constantE(function(x) { return x + 1; })

Xi Chen

unread,
May 21, 2009, 10:08:48 AM5/21/09
to fla...@googlegroups.com
Hi Artyom,

Thanks a lot for your informative reply! It helps a lot. I think the core of what you are trying to say is exactly "By thinking harder about the relationships between your data you write more declarative specifications, leaving more of the work of maintaining consistency to the language" in flapjax tutorial.

However, lets back to my concrete problem. Heres some my thoughts:

* div block position fully depends on its topleft (x,y)
* (x,y) must be a behavior result
* i have to detect what arrow keys people pressed to decide what (x,y) value should behave

and actually after a few attempts, I make it working.

First make an event stream which is to capture all keydown events on window itself:

var keysE = extractEventE(self, 'keydown');

then transform this event stream:

var move = keysE.mapE(function(p){
        switch (p.keyCode) {
            case 37:    // user press '<---'
                x = x - 24;
                return x;
                break;
            case 39:   // user press '--->'
                x = x + 24;
                return x;
                break;
            default:
                break;
        }       
    });

lastly I render it:

insertDomB(DIV({
            className: 'block',
            style: {
                position: 'absolute',
                left: move.startsWith(x),   // currently i'm only making it can be moved left or right
                top: y,
                width: this.width,   // my class Block attribute
                height: this.height,
                background: this.color
            }
        }, ""), 'area', 'beginning');


It works fine. BUT, I still feel a bit guilty :-s - because of my 'var move'. No matter how I inspect it, it looks quite like traditional callbacks: detect key code then make a decision what to do next. However, I think it's the key business logic for this problem isnt it. What makes me relieved is my block's topleft corner is exactly a flapjax behavior result.

I'd like to hear any tips to do it in a more decent way.

2009/5/21 Artyom Shalkhakov <artyom.s...@gmail.com>



--
Best Regards,

Xi Chen

Arjun Guha

unread,
May 23, 2009, 3:59:39 PM5/23/09
to fla...@googlegroups.com
I assume x is a global variable that is initialized to the the initial
position of the block. I recommend you look at the collectE function.
Turn that mapE into a collectE and it will no longer use global
state.

This will let you create multiple moveE's (if you move that logic into
a function) that are initialized with different starting positions.
That will give turn this into a reusable abstraction: perhaps that's
what you're looking for?

Arjun

Reply all
Reply to author
Forward
0 new messages