Search code examples
javascriptreactive-programmingfrpbacon.js

How to make use of scan() more clear


I want to make a simple web page using bacon.js. It should have a button which toggles a boolean state by mouse click. After setting up the streams, the app should be initialized by sending an object to an init stream. Here i just send the desired initial boolean state of the toggle (false). The code looks like this (i faked the button clicks and initialization):

// Helper function
var neg = function (a) { return !a; }; 

// Fake of initialization stream. End after one element
var init = Bacon.sequentially(500, [false]);  
// Fake of button click event stream.
var click = Bacon.repeatedly(1000, [{}]); 

var toggle = init.concat(click).scan(null, 
    function(a, b) { return a === null ? b : neg(a); }).skip(1).toEventStream();

toggle.log();
// Output: false, true, false, true, ....

The above code (jsFiddle) works as expected, but i do not like that the toggle stream creation line is so complex.

If it would be possible to omit the seed value (take it from the stream) and if scan returns an EventStream instead of a Property, i could write:

var toggle = init.concat(click).scan(neg);

This could be read much nicer. Or if it would be possible to give the seed as stream i could just write:

var toggle = click.scan(init, neg);

Do you have any suggestions to make the code more clear? Is there an alternative solution to using scan? Should i make me an own scan method?


Solution

  • If you read what I wrote previously, ignore. it. :) This situation is exactly what flatMap is for: streams that create streams. Use flatMap.

    // Helper function
    var neg = function (a) { return !a; };
    
    // Fake of initialization stream. End after one element
    // Note the use of `later` instead of `sequentially`, by the way
    var init = Bacon.later(500, false);
    
    // Fake of button click event stream.
    var click = Bacon.repeatedly(1000, [null]);
    
    var toggle = init
        .flatMap(function(initVal) { return click.scan(initVal, neg) });
    
    toggle.log();