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

Functional Reactive Programming

Functional Reactive Programming

NodePDX 2013

Christopher Meiklejohn

May 17, 2013
Tweet

More Decks by Christopher Meiklejohn

Other Decks in Programming

Transcript

  1. 1 2 3 4 5 6 7 8 9 10

    11 12 13 14 15 16 17 18 function handleClick(ev) { return computed = compute(ev.context); } Thursday, May 16, 13
  2. 1 2 3 4 5 6 7 8 9 10

    11 12 13 14 15 16 17 18 function handleClick(ev) { // jQuery call to change background color } function handleMouseOver(ev) { // jQuery call to change background color } Thursday, May 16, 13
  3. 1 2 3 4 5 6 7 8 9 10

    11 12 13 14 15 16 17 18 function handleClick(ev) { changeBackgroundColor(...); } function handleMouseOver(ev) { changeBackgroundColor(...); } function changeBackgroundColor(color) { // jQuery call to change background color } Thursday, May 16, 13
  4. A = B + C C = D + E

    Thursday, May 16, 13
  5. A = B + C C = D + E

    A B C D E Thursday, May 16, 13
  6. A = B + C C = LIFT2 + D,

    E A B C D E Thursday, May 16, 13
  7. var a = y + 0; var b = y

    + a; var c = b + 1; var d = c % 2; var e = 5 / d; A B C Y D E Thursday, May 16, 13
  8. SEND HOLD MERGE CONSTANT CHANGES BIND SWITCH MAP NOT DELAY

    COLLECT FILTER SEND ONCE LIFT SKIP-FIRST FILTER-REPEATS SNAPSHOT Thursday, May 16, 13
  9. • Language and Framework • Compiler in Haskell • Brown

    University 2009 (OOPSLA 2009) • First major implementation in JavaScript FLAPJAX Thursday, May 16, 13
  10. 1 2 3 4 5 6 7 8 9 10

    11 12 13 14 15 16 17 18 var tenthB = timerB(100); Thursday, May 16, 13
  11. 1 2 3 4 5 6 7 8 9 10

    11 12 13 14 15 16 17 18 var tenthB = timerB(100); var secondB = Math.floor(tenthB / 1000); Thursday, May 16, 13
  12. 1 2 3 4 5 6 7 8 9 10

    11 12 13 14 15 16 17 18 var tenthB = timerB(100); var secondB = Math.floor(tenthB / 1000); var hashE = timerE(50).mapE(function() { return window.location.hash; }); Thursday, May 16, 13
  13. 1 2 3 4 5 6 7 8 9 10

    11 12 13 14 15 16 17 18 var tenthB = timerB(100); var secondB = Math.floor(tenthB / 1000); var hashE = timerE(50).mapE(function() { return window.location.hash; }); var routeE = hashE.mapE(function(h) { return h ? h.slice(1) : defaultRoute }); Thursday, May 16, 13
  14. 1 2 3 4 5 6 7 8 9 10

    11 12 13 14 15 16 17 18 var tenthB = timerB(100); var secondB = Math.floor(tenthB / 1000); var hashE = timerE(50).mapE(function() { return window.location.hash; }); var routeE = hashE.mapE(function(h) { return h ? h.slice(1) : defaultRoute }); var routeB = startsWith(filterRepeatsE(routeE), defaultRoute); Thursday, May 16, 13
  15. 1 2 3 4 5 6 7 8 9 10

    11 12 13 14 15 16 17 18 var startEditEe = extractEventE(label, 'dblclick'). mapE(function() { li.className = 'editing'; edit.focus(); return extractValueE(edit); }); var stopEditEe = mergeE(returnPressE(edit), blurE(edit)). mapE(function() { li.className = todo.completed ? 'completed' : ''; return oneE(sentinel); }); var eventsE = switchE(mergeE(startEditEe, stopEditEe)); Thursday, May 16, 13
  16. RXJS (Reactive Extensions) • Authored by the Cloud Programmability Group

    at Microsoft • Set of libraries • Bindings in various languages providing “reactivity” • Observables and observers • Provides scheduling and LINQ for querying Thursday, May 16, 13
  17. 1 2 3 4 5 6 7 8 9 10

    11 12 13 14 15 16 17 18 /* Only get the value from each key up */ var keyups = Rx.Observable.fromEvent(input, 'keyup') .select(function (e) { return e.target.value; }) .where(function (text) { return text.length > 2; }); Thursday, May 16, 13
  18. 1 2 3 4 5 6 7 8 9 10

    11 12 13 14 15 16 17 18 /* Only get the value from each key up */ var keyups = Rx.Observable.fromEvent(input, 'keyup') .select(function (e) { return e.target.value; }) .where(function (text) { return text.length > 2; }); /* Now throttle/debounce the input for 500ms */ var throttled = keyups .throttle(500 /* ms */); Thursday, May 16, 13
  19. 1 2 3 4 5 6 7 8 9 10

    11 12 13 14 15 16 17 18 /* Only get the value from each key up */ var keyups = Rx.Observable.fromEvent(input, 'keyup') .select(function (e) { return e.target.value; }) .where(function (text) { return text.length > 2; }); /* Now throttle/debounce the input for 500ms */ var throttled = keyups .throttle(500 /* ms */); /* Now get only distinct values, so we eliminate the arrows */ var distinct = keyups .distinctUntilChanged(); Thursday, May 16, 13
  20. 1 2 3 4 5 6 7 8 9 10

    11 12 13 14 15 16 17 18 function searchWikipedia(term) { var url = 'http://en.wikipedia.org/w/api.php?action=opensearch' + '&format=json' + '&search=' + encodeURI(term); return Rx.Observable.getJSONPRequest(url); } Thursday, May 16, 13
  21. 1 2 3 4 5 6 7 8 9 10

    11 12 13 14 15 16 17 18 function searchWikipedia(term) { var url = 'http://en.wikipedia.org/w/api.php?action=opensearch' + '&format=json' + '&search=' + encodeURI(term); return Rx.Observable.getJSONPRequest(url); } var suggestions = distinct .select(function (text) { return searchWikipedia(text); }) .switchLatest() .where(function (data) { return data.length == 2 && data[1].length > 0; }); Thursday, May 16, 13
  22. 1 2 3 4 5 6 7 8 9 10

    11 12 13 14 15 16 17 18 var suggestions = distinct .select(function (text) { return searchWikipedia(text); }) .switchLatest() .where(function (data) { return data.length == 2 && data[1].length > 0; }); suggestions.subscribe( function (data) { var results = data[1]; /* Do something with the data like binding */ }, function (e) { /* handle any errors */ }); Thursday, May 16, 13
  23. BACON.JS • Inspired by Reactive Banana and RxJS • Flowdock,

    open sourced in 2013 Thursday, May 16, 13
  24. 1 2 3 4 5 6 7 8 9 10

    11 12 13 14 15 16 17 18 var clicks = $("h1").asEventStream("click"); clicks.onValue(function() { alert("you clicked the h1 element") }); Thursday, May 16, 13
  25. 1 2 3 4 5 6 7 8 9 10

    11 12 13 14 15 16 17 18 var clicks = $("h1").asEventStream("click"); clicks.onValue(function() { alert("you clicked the h1 element") }); var plus = $("#plus").asEventStream("click").map(1); var minus = $("#minus").asEventStream("click").map(-1); var both = plus.merge(minus); Thursday, May 16, 13
  26. 1 2 3 4 5 6 7 8 9 10

    11 12 13 14 15 16 17 18 var clicks = $("h1").asEventStream("click"); clicks.onValue(function() { alert("you clicked the h1 element") }); var plus = $("#plus").asEventStream("click").map(1); var minus = $("#minus").asEventStream("click").map(-1); var both = plus.merge(minus); function add(x, y) { return x + y }; var counter = both.scan(0, add); counter.onValue(function(sum) { $("#sum").text(sum) }); Thursday, May 16, 13
  27. 1 2 3 4 5 6 7 8 9 10

    11 12 13 14 15 16 17 18 var clicks = $("h1").asEventStream("click"); clicks.onValue(function() { alert("you clicked the h1 element") }); var plus = $("#plus").asEventStream("click").map(1); var minus = $("#minus").asEventStream("click").map(-1); var both = plus.merge(minus); function add(x, y) { return x + y }; var counter = both.scan(0, add); counter.onValue(function(sum) { $("#sum").text(sum) }); function f(a, b) { console.log(a + b) } Bacon.onValues(Bacon.constant(1), Bacon.constant(2), f) Thursday, May 16, 13
  28. 1 2 3 4 5 6 7 8 9 10

    11 12 13 14 15 16 17 18 var activity = $(window).asEventStream('mousemove keydown') var active = activity.flatMapLatest(function(event) { return Bacon.once(true).merge(Bacon.later(30000, false)); }).toProperty(true); Thursday, May 16, 13
  29. RXJS-NODE • Reified interfaces • Creates asynchronous objects for receiving

    events • node, child_process, dns, events, fs, http, https, net, path, process, sys Thursday, May 16, 13
  30. 1 2 3 4 5 6 7 8 9 10

    11 12 13 14 15 16 17 18 exports.get = function (options) { var subject = new Rx.AsyncSubject(), handler = handler = function (response) { subject.onNext(response); subject.onCompleted(); }, errHandler = function (err) { subject.onError(err); }; http.get(options, handler).on('error', errHandler); return subject; }; Thursday, May 16, 13