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

The Joys of Functional Programming

The Joys of Functional Programming

The fun that can be had in functional programming, from someone who is not a functional programmer.

Stephen Mizell

February 09, 2015
Tweet

More Decks by Stephen Mizell

Other Decks in Programming

Transcript

  1. First, Some Caveats 1. I do not do 100% functional

    programming 2. I will be showing examples in Javascript 3. I do not currently use functional reactive programming
  2. What is Functional Programming? 1. Imperative vs. Declarative 2. Persistent

    and Immutable Data 3. No Side Effects 4. Noun vs. Verb 5. First class and higher order functions 6. Recursive over iterative
  3. Iterative Factorial var num = 5, total = 1; if

    (num !== 0) { for (i = 1; i <= num; i++) { console.log(i); total = total * i; } } console.log(total);
  4. Recursive Factorial var num = 5; function factorial(n) { if

    (n === 0) return 1; return n * factorial(n - 1); } console.log(factorial(num));
  5. Imperative vs. Declarative Imperative you express how to do it,

    declarative you express what you want done
  6. Imperative var list = [1, 2, 3, 4, 5], total

    = 0; for (i = 0; i < list.length; i++) { total += list[i]; }
  7. Declarative (Part 1) var list = [1, 2, 3, 4,

    5]; var total = list.reduce(function(result, num) { return result + num; });
  8. Declarative (Part 2) var list = [1, 2, 3, 4,

    5]; function add(a, b) { return a + b; } var total = list.reduce(add);
  9. Declarative (Part 3) var list = [1, 2, 3, 4,

    5]; function add(a, b) { return a + b; } function sum(items) { return items.reduce(add); } var total = sum(list);
  10. Here's Some Data var players = [ { totalPoints: 2031,

    games: 30 }, { totalPoints: 4268, games: 45 }, { totalPoints: 2235, games: 24 }, { totalPoints: 1221, games: 22 }, { totalPoints: 5420, games: 40 } ];
  11. Requirements 1. Get a list of the average points for

    each player 2. Get an average for all players who have played 30 or more games
  12. Imperative var playerAverages = [], allPoints = 0, allGames =

    0; for (i = 0; i < players.length; i++) { var average = players[i].totalPoints / players[i].games; playerAverages.push(average); if (players[i].games >= 30) { allPoints += players[i].totalPoints; allGames += players[i].games; } } var totalAverage = allPoints / allGames;
  13. Declarative (Part 1) var playerAverages = players.map(function(player) { return player.totalPoints

    / player.games; }); var players30Games = players.filter(function(player) { return player.games >= 30 }); var points = players30Games.map(function(player) { return player.totalPoints; }); var games = players30Games.map(function(player) { return player.games; }); var totalPoints = sum(points), totalGames = sum(games); totalAverage = totalPoints / totalGames;
  14. Declarative (Part 2) function pluck(items, prop) { return items.map(function(item) {

    return item[prop]; }); } function average(a, b) { return a / b; } var playerAverages = players.map(function(player) { return player.totalPoints / player.games; }); var players30Games = players.filter(function(player) { return player.games >= 30 }); var totalPoints = sum(pluck(players30Games, 'totalPoints')), totalGames = sum(pluck(players30Games, 'games')); totalAverage = average(totalPoints, totalGames);
  15. Currying Currying is the technique of translating the evaluation of

    a function that takes multiple arguments (or a tuple of arguments) into evaluating a sequence of functions, each with a single argument (partial application). –Wikipedia
  16. Currying function add(a) { return function(b) { return a +

    b; } } var add10 = add(10); console.log(add10(5)); // 15
  17. Currying (Better Example) var fullName = _.curry(function(first, middle, last) {

    return [first, middle, last].join(' '); }); var johnA = fullName('John', 'A.'); var johnASmith = johnA('Smith'); // John A. Smith
  18. Currying (From Player Data Example) // Uses a library called

    Ramda var players30Games = R.filter(R.pipe(R.get('games'), R.lte(30))); var totalPoints = R.pipe(players30Games, R.pluck('totalPoints'), R.sum); totalPoints(players)
  19. How is declarative helpful? 1. Testable, maintainable, evolvable code 2.

    Writing code at a higher level of abstraction 3. Through composition, it can lead to a very powerful way of thinking (like SQL)
  20. Persistent State 1. Values are immutable 2. A "change" in

    a value returns a new value (old is never thrown away) 3. These values persist (not necessarily on disk, but throughout time) 4. Big plus: fast!
  21. Crude Example A -> B -> C Sharing all of

    A Z -> A -> B -> C Sharing tail of A Y -> B -> C
  22. Persistence 1. Shared memory usage 2. No need to do

    deep clones of objects to avoid changing state 3. Allows for immutable data 4. Allows for understanding what has changed 5. Allows for no side effects
  23. Side Effects var x = 10; function a() { x

    = 4; } a(); console.log(x);
  24. Why is persistence and immutability good? 1. We've probably all

    spent hours chasing down bugs from side effects 2. Code that mutates data requires following steps through code to understand what it is doing 3. Mutating lots of code can be expensive
  25. Libraries for Javascript 1. Underscore.js and Lo-Dash.js provide some functions

    2. Ramda - Function-first library 3. Mori - Immutable data structures in Javascript 4. React.js and Immutable.js 5. ClojureScript
  26. Recap, why is this helpful? 1. Code is more declarative

    2. Promotes reusability and evolvability 3. Easier to write automated tests 4. Fewer bugs
  27. FRP There is no simple definition of this on the

    internet, so here are some qualities. 1. Deal with events in a functional way 2. Create streams of events rather than event listeners 3. Free of side effects
  28. Bacon.js function movieSearch(query) { if (query.length < 3) // show

    no results for queries of length < 3 return Bacon.once([]); return Bacon.fromPromise(queryMovie(query)); } var text = $('#input') .asEventStream('keydown') .debounce(300) .map(function(event) { return event.target.value; }) .skipDuplicates(); // Only react to latest, in case they are out of order var suggestions = text.flatMapLatest(movieSearch); text.awaiting(suggestions).onValue(function(x) { if (x) $('#results').html('Searching...'); }); suggestions.onValue(function(results) { $('#results').html($.map(results, showMovie)); });