Slide 1

Slide 1 text

Functional Reactive Programming Josh Bassett in JavaScript

Slide 2

Slide 2 text

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.

Slide 3

Slide 3 text

Functional Programming

Slide 4

Slide 4 text

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).

Slide 5

Slide 5 text

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

Slide 6

Slide 6 text

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]

Slide 7

Slide 7 text

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).

Slide 8

Slide 8 text

Map - Applies a function to every element in a data structure and returns a new data structure. - Array.prototype.map function in JS.

Slide 9

Slide 9 text

Map function inc(a) { return a + 1 } [1, 2, 3].map(inc) => [2, 3, 4]

Slide 10

Slide 10 text

Filter - Filters every element in a data structure using predicate and returns a new data structure. - Array.prototype.filter function in JS.

Slide 11

Slide 11 text

Filter function odd(a) { return a % 2 != 0 } [1, 2, 3].filter(odd) => [1, 3]

Slide 12

Slide 12 text

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.

Slide 13

Slide 13 text

Fold function add(a, b) { return a + b }
 [1, 2, 3].reduce(add, 0) => 6
 [1, 2, 3].reduceRight(add, 0) => 6

Slide 14

Slide 14 text

Scan - Reduces a data structure by combining its elements using a binary function and returns the intermediate results. - Not implemented in native JS

Slide 15

Slide 15 text

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]

Slide 16

Slide 16 text

Functional Reactive Programming for great good

Slide 17

Slide 17 text

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.

Slide 18

Slide 18 text

FRP in JavaScript

Slide 19

Slide 19 text

Streams - Models flow of data with streams. - A stream is a functional data structure (supports combinators like map, filter, fold, scan, etc).

Slide 20

Slide 20 text

Streams var s = Bacon.fromEventTarget(document.body, ‘click’) var s = $(‘#button’).asEventStream(‘click’)
 s.onValue(function(v) { console.log(v) })
 s.log()

Slide 21

Slide 21 text

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

Slide 22

Slide 22 text

Properties var property = stream.toProperty() property.assign($(‘.label’), ‘text’)

Slide 23

Slide 23 text

Buses var a = new Bacon.Bus() var b = new Bacon.Bus()
 a.log() a.plug(b)
 a.push(‘hello’) => hello
 b.push(‘world’) => world

Slide 24

Slide 24 text

React & Bacon

Slide 25

Slide 25 text

Components

Slide 26

Slide 26 text

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.

Slide 27

Slide 27 text

Number Field

Slide 28

Slide 28 text

Number Field var NumberFieldComponent = React.createClass({ render: function() { return (
{this.props.value} ⬆️ ⬇️
); }, handleUp: function() { this.props.bus.push(‘up’); }, handleDown: function() { this.props.bus.push(‘down’); } });

Slide 29

Slide 29 text

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);

Slide 30

Slide 30 text

Number Field sum.onValue(function(value) {
 React.renderComponent(
 NumberFieldComponent({
 bus: bus,
 value: value
 }),
 document.body
 );
 });

Slide 31

Slide 31 text

State Transformer

Slide 32

Slide 32 text

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.

Slide 33

Slide 33 text

State Transformer var state = bus.scan( new MyState(), transformState );

Slide 34

Slide 34 text

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; }

Slide 35

Slide 35 text

App Pipeline

Slide 36

Slide 36 text

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.

Slide 37

Slide 37 text

Memory

Slide 38

Slide 38 text

Memory function Game(cards) { this.cards = cards; this.selectedCards = []; } function Card(shape) { this.shape = shape; this.disabled = false; }

Slide 39

Slide 39 text

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);

Slide 40

Slide 40 text

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; }

Slide 41

Slide 41 text

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; };

Slide 42

Slide 42 text

Memory Game.prototype.addSelectedCard = function(card) { return React.addons.update(this, { selectedCards: {$push: [card]} }); };

Slide 43

Slide 43 text

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

Slide 44

Slide 44 text

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

Slide 45

Slide 45 text

What Next?