Bacon.js - FRP (functional reactive programming) library for Javascript

Intro

The idea of Functional Reactive Programming is quite well described by Conal Elliot at Stack Overflow.

Bacon.js is a library for functional reactive programming. Or let's say it's a library for working with events and dynamic values (which are called Properties in Bacon.js).

Anyways, you can wrap an event source, say "mouse clicks on an element" into an EventStream by saying

var cliks = $("h1").asEventStream("click")

Each EventStream represents a stream of events. It is an Observable object, meaning that you can listen to events in the stream using, for instance, the onValue method with a callback. Like this:

cliks.onValue(function() { alert("you clicked the h1 element") })

But you can do neater stuff too. The Bacon of bacon.js is in that you can transform, filter and combine these streams in a multitude of ways (see API below). The methods map, filter, for example, are similar to same functions in functional list programming (like Underscore). So, if you say

var plus = $("#plus").asEventStream("click").map(1)
var minus = $("#minus").asEventStream("click").map(-1)
var both = plus.merge(minus)

.. you'll have a stream that will output the number 1 when the "plus" button is clicked and another stream outputting -1 when the "minus" button is clicked. The both stream will be a merged stream containing events from both the plus and minus streams. This allows you to subscribe to both streams with one handler:

both.onValue(function(val) { /* val will be 1 or -1 */ })

In addition to EventStreams, bacon.js has a thing called Property, that is almost like an EventStream, but has a "current value". So things that change and have a current state are Properties, while things that consist of discrete events are EventStreams. You could think mouse clicks as an EventStream and mouse position as a Property. You can create Properties from an EventStream with scan or toProperty methods. So, let's say

function add(x, y) { return x + y }
var counter = both.scan(0, add)
counter.onValue(function(sum) { $("#sum").text(sum) })
Example

The counter property will contain the sum of the values in the both stream, so it's practically a counter that can be increased and decreased using the plus and minus buttons. The scan method was used here to calculate the "current sum" of events in the both stream, by giving a "seed value" 0 and an "accumulator function" add. The scan method creates a property that starts with the given seed value and on each event in the source stream applies the accumulator function to the current property value and the new value from the stream.

Properties can be very conveniently used for assigning values and attributes to DOM elements with JQuery. Here we assign the value of a property as the text of a span element whenever it changes:

property.assign($("span"), "text")

Hiding and showing the same span depending on the content of the property value is equally straightforward

function hiddenForEmptyValue(value) { return value == "" ? "hidden" : "visible" }
property.map(hiddenForEmptyValue).assign($("span"), "css", "visibility")

In the example above a property value of "hello" would be mapped to "visible", which in turn would result in Bacon calling

$("span").css("visibility", "visible")