Functional Reactive Programming

Functional Reactive Programming

NodePDX 2013

3e09fee7b359be847ed5fa48f524a3d3?s=128

Christopher Meiklejohn

May 17, 2013
Tweet

Transcript

  1. 5.

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

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

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

    A = B + C C = D + E

    Thursday, May 16, 13
  5. 33.

    A = B + C C = D + E

    A B C D E Thursday, May 16, 13
  6. 34.

    A = B + C C = LIFT2 + D,

    E A B C D E Thursday, May 16, 13
  7. 35.

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

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

    • Language and Framework • Compiler in Haskell • Brown

    University 2009 (OOPSLA 2009) • First major implementation in JavaScript FLAPJAX Thursday, May 16, 13
  10. 41.

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

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

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

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

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

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

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

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

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

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

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

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

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

    BACON.JS • Inspired by Reactive Banana and RxJS • Flowdock,

    open sourced in 2013 Thursday, May 16, 13
  24. 57.

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

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

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

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

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

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

    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