Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Functional Reactive Programming in JavaScript

Functional Reactive Programming in JavaScript

In this talk I give an intro to functional reactive programming with the Bacon.js library.

Josh Bassett

August 13, 2014

Transcript

45 slides
  1. Why Should You Care? - Writing code is easy (for

    a programmer). - Understanding (fixing/changing) code is hard. - There is inherent complexity in the way we choose to solve a problem. - FRP allows us to reduce the inherent complexity of our code.
  2. Pure Functions - Any function which always returns the same

    output, given the same input. - Calling the function does not cause any observable side effects (e.g. mutation of mutable objects).
  3. Pure Functions function add(a, b) { return a + b

    }
 add(1, 2) => 3
 var c = 1 // external state function add(a, b) { return a + b + c } add(1, 2) => 4
 c = 2 add(1, 2) // same input => 5
  4. Pure Functions var a = [1, 2, 3]
 a.slice(1) =>

    [2, 3] a => [1, 2, 3]
 a.reverse() => [3, 2, 1] a => [3, 2, 1]
  5. Immutability - An immutable object is an object whose state

    can’t be changed. - Can’t be enforced in JS, convention only. - Use POJOs and copy them on update (e.g. use React.addons.update function). - Use a persistent data structure library (e.g. mori, immutable).
  6. Map - Applies a function to every element in a

    data structure and returns a new data structure. - Array.prototype.map function in JS.
  7. Map function inc(a) { return a + 1 } [1,

    2, 3].map(inc) => [2, 3, 4]
  8. Filter - Filters every element in a data structure using

    predicate and returns a new data structure. - Array.prototype.filter function in JS.
  9. Filter function odd(a) { return a % 2 != 0

    } [1, 2, 3].filter(odd) => [1, 3]
  10. Fold - Reduces a data structure by combining its elements

    using a binary function and returns a single value. - Array.prototype.reduce & Array.prototype.reduceRight functions in JS.
  11. Fold function add(a, b) { return a + b }


    [1, 2, 3].reduce(add, 0) => 6
 [1, 2, 3].reduceRight(add, 0) => 6
  12. Scan - Reduces a data structure by combining its elements

    using a binary function and returns the intermediate results. - Not implemented in native JS
  13. Scan function add(a, b) { return a + b }


    [1, 2, 3].scan(add, 0) => [0, 1, 3, 6]
 [1, 2, 3].scanRight(add, 0) => [6, 3, 1, 0]
  14. FRP - Paradigm from FP community. - Focus on behaviour

    over implementation. - Models flow of data in a system using functional data structures called streams. - Avoids callback hell with FP combinators. - Libraries available for many languages.
  15. Streams - Models flow of data with streams. - A

    stream is a functional data structure (supports combinators like map, filter, fold, scan, etc).
  16. Streams function add(a, b) { return a + b }


    var up = $(‘#up’).asEventStream(‘click’) var down = $(‘#down’).asEventStream(‘click’) var delta = up.map(1).merge(down.map(-1)) var sum = delta.scan(0, add)
 sum.log() => 0 => 1 => 2 => 1 => etc
  17. Buses var a = new Bacon.Bus() var b = new

    Bacon.Bus()
 a.log() a.plug(b)
 a.push(‘hello’) => hello
 b.push(‘world’) => world
  18. Components - Instantiate the component with a reference to the

    bus (via props). - Component handles UI events and pushes them onto the bus. - The bus streams events from the UI back to our app logic.
  19. Number Field var NumberFieldComponent = React.createClass({ render: function() { return

    ( <div> <span>{this.props.value}</span> <a onClick={this.handleUp}>⬆️</a> <a onClick={this.handleDown}>⬇️</a> </div> ); }, handleUp: function() { this.props.bus.push(‘up’); }, handleDown: function() { this.props.bus.push(‘down’); } });
  20. Number Field function add(a, b) { return a + b;

    }
 var bus = new Bacon.Bus(); var up = bus.filter(function(e) {
 return e === ‘up’;
 }); var down = bus.filter(function(e) {
 return e === ‘down’;
 }); var delta = up.map(1).merge(down.map(-1)); var sum = delta.scan(0, add);
  21. State Transformer - A state transformer is a pure function

    which takes a state and an input value as arguments and returns a new state. - It’s a binary function which can be scanned over a stream of input events to produce a stream of state objects.
  22. State Transformer function transformState(state, input) { if (input.type === 'a')

    { state = state.foo(input.data); } else if (input.type === 'b') { state = state.bar(input.data); } return state; }
  23. App Pipeline - The bus streams input events from the

    UI. - The state transformer is scanned over the bus to produce a state object stream. - The React.renderComponent function is applied to the state object value to produce a new VDOM.
  24. Memory function Game(cards) { this.cards = cards; this.selectedCards = [];

    } function Card(shape) { this.shape = shape; this.disabled = false; }
  25. Memory var game = new Game(cards);
 var bus = new

    Bacon.Bus(),
 gameState = bus.scan(game, transformState),
 selectedCards = gameState.map(‘.selectedCards');
 var deselectCards = selectedCards
 .filter(function(a) { return a.length >= 2; })
 .map(function() { return {type: 'deselect-cards'}; })
 .debounce(1000);
 bus.plug(deselectCards);
  26. Memory function transformState(game, event) { if (event.type === 'select-card') {

    game = game.selectCard(event.card); } else if (event.type === 'deselect-cards') { game = game.deselectAllCards(); } return game; }
  27. Memory Game.prototype.selectCard = function(card) { if (_.contains(this.selectedCards, card)) return this;

    if (this.selectedCards.length >= 2) return this; var game = this.addSelectedCard(card); if (game.selectedCardsAreMatching()) { game = game.disableSelectedCards(); } return game; };

More Decks by Josh Bassett

Other Decks in Programming