Zac Anger's Blog



Tags: bacon, js, frp, streams, events, functional, programming, reactive, fp

Note: this post references 'the snake game' a lot. That's over, now.

Bacon is a JS lib for FRP, like Rx, but not MS.

Here's some jQuery, because I don't know why.

var go = $('#clickMe').asEventStream('click')
go.onValue (
  () => $('#output').append('clicked!'))

Is it just me or is this tutorial using typescript? Eww, it totally is! Gross.

scan combinator is kinda like reduce, except async and produces multiple values. So, there's an initial value, and a function to combine them. Returned stream contains the aggregate.

var clicks = $('#example button').asEventStream('click')
  , counter = clicks
      .scan(0, (x,y) => x + y)
counter.onValue(x => $('#example .output').html(x))

Ohh, okay, so the 'take' and 'skip' combinators are new. They do what they sound like. Basically like slicing arrays, but with streams.

Since we're avoiding side-effects (yo, this is functional programming), we instead use event switching. What that means is, when x, do y; for our case (at least right now?), on event x, start event-stream y. With Bacon, we'll be mostly using flatMapLatest to handle things here. It takes an event stream (x), and from each x event, maps it to a new stream (f(x)), for function f. stream.flatMapLatest(x => Bacon.sequentially(100, [x, x+1, x+2])) maps the numeric x events to the stream [x, x+1, x+2]. The delays here mean that the next even from source x occurs before x+2, so that's skipped. Only on the last event, after which there are no more x events, would all source x events appear in the returned stream. So, in other words, when event x occurs in source steam (x), the output becomes (or switches!) to f(x) (again, for function f()).

Note (in the snake game) the difference between how we handle the apple and how we'd handle it imperatively. In 'traditional' (lol) programming, we'd be updating that from several places in the code, reacting to events. Supposedly that 'feels more natural' to people? I don't know. I guess I haven't been programming long enough to feel more natural doing things imperatively.

Event streams are kinda like spreadsheets? Eww, spreadsheets. But it makes sense, I guess. A1 = B1 + C1; here A1 is totally defined in terms of other 'cells' rather than being updated from an external source.

Okay, so implementing our own combinators is pretty nice. We add to Bacon so we can keep using this little DSL. See the snake game, the bit where we change window size, for how this is done.

NOTE: snake-game.bak.js is where the actual attempts at learning ended up, with lots of comments. snake-game.js is the final version, cleaned up and less ugly and weird and whatever.