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.

128a23ed95e352275c88fcb39c888932?s=128

Josh Bassett

August 13, 2014
Tweet

Transcript

  1. Functional Reactive Programming Josh Bassett in JavaScript

  2. 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.
  3. Functional Programming

  4. 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).
  5. 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
  6. 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]
  7. 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).
  8. Map - Applies a function to every element in a

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

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

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

    } [1, 2, 3].filter(odd) => [1, 3]
  12. 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.
  13. Fold function add(a, b) { return a + b }


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

    using a binary function and returns the intermediate results. - Not implemented in native JS
  15. 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]
  16. Functional Reactive Programming for great good

  17. 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.
  18. FRP in JavaScript

  19. Streams - Models flow of data with streams. - A

    stream is a functional data structure (supports combinators like map, filter, fold, scan, etc).
  20. Streams var s = Bacon.fromEventTarget(document.body, ‘click’) var s = $(‘#button’).asEventStream(‘click’)


    s.onValue(function(v) { console.log(v) })
 s.log()
  21. 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
  22. Properties var property = stream.toProperty() property.assign($(‘.label’), ‘text’)

  23. Buses var a = new Bacon.Bus() var b = new

    Bacon.Bus()
 a.log() a.plug(b)
 a.push(‘hello’) => hello
 b.push(‘world’) => world
  24. React & Bacon

  25. Components

  26. 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.
  27. Number Field

  28. 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’); } });
  29. 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);
  30. Number Field sum.onValue(function(value) {
 React.renderComponent(
 NumberFieldComponent({
 bus: bus,
 value: value


    }),
 document.body
 );
 });
  31. State Transformer

  32. 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.
  33. State Transformer var state = bus.scan( new MyState(), transformState );

  34. 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; }
  35. App Pipeline

  36. 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.
  37. Memory

  38. Memory function Game(cards) { this.cards = cards; this.selectedCards = [];

    } function Card(shape) { this.shape = shape; this.disabled = false; }
  39. 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);
  40. 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; }
  41. 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; };
  42. Memory Game.prototype.addSelectedCard = function(card) { return React.addons.update(this, { selectedCards: {$push:

    [card]} }); };
  43. Memory gameState.onValue(function(game) { React.renderComponent( GameComponent({bus: bus, game: game}), document.body );

    });
  44. What’s Next? - http://joshbassett.info/2014/reactive-uis-with- react-and-bacon/ - https://github.com/nullobject/frpjs-examples

  45. What Next?