Functional Patterns for the non-mathematician

4,162 views
3,933 views

Published on

Fluentconf 2014 talk:

Functional design patterns such as lenses, arrows, functors, and monads all come from category theory. To fully grok them, you’ll probably have to wade through the whitest white papers, fighting the mathematical syntax and abstract examples.

I’m hoping to demonstrate the ideas into javascript. I’ll be showing direct and practical applications for every day programming.

Published in: Technology
0 Comments
16 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
4,162
On SlideShare
0
From Embeds
0
Number of Embeds
144
Actions
Shares
0
Downloads
59
Comments
0
Likes
16
Embeds 0
No embeds

No notes for slide

Functional Patterns for the non-mathematician

  1. 1. Functional Patterns for the non-mathematician
  2. 2. add(4, 2) //=> 6
  3. 3. // associative add(add(1, 2), 4) == add(1, add(2, 4)) // commutative add(4, 1) == add(1, 4) // identity add(n, 0) == n // distributive multiply(2, add(3,4)) == add(multiply(2, 3), multiply(2, 4))
  4. 4. add(4.4, 2.2) //=> 6.6
  5. 5. var inc = new Increaser(4); inc.increaseBy(2); inc.value(); // 6
  6. 6. Interfaces Properties/Laws Polymorphic Composable
  7. 7. Currying
  8. 8. var reverseCap = compose(capitalize, reverse) reverseCap(“hello”) //=> “Olleh” Composition
  9. 9. var reverseCap = compose(capitalize, reverse) reverseCap(“hello”) //=> “Olleh” Composition
  10. 10. var reverseCap = compose(capitalize, reverse)(“hello”) //=> “Olleh” Composition “hello”“olleh”“Olleh”
  11. 11. compose(compose(f, g), h) == compose(f, compose(g, h)) Composition (associativity)
  12. 12. compose(f, g, h) Composition (associativity)
  13. 13. var i = compose(g, h) compose(f, i) Composition (associativity)
  14. 14. var getFromDb = compose(pluck('rows'), User.findAll) var cleanUpData = compose(capitalize, pluck('name')) var renderTemplate = TemplateEngine.render(‘users_table') var makePage = compose(renderTemplate, map(cleanUpData), getFromDb) makePage({limit: 20}) makePage({limit: 20}) makePage({limit: 20}) makePage({limit: 20}) makePage({limit: 20}) makePage({limit: 20}) makePage({limit: 20}) makePage({limit: 20}) makePage({limit: 20})
  15. 15. var getFromDb = compose(pluck('rows'), User.findAll) var cleanUpData = compose(capitalize, pluck('name')) var renderTemplate = TemplateEngine.render(‘users_table') var makePage = compose(renderTemplate, map(cleanUpData), getFromDb) makePage({limit: 20}) makePage({limit: 20}) makePage({limit: 20}) makePage({limit: 20}) makePage({limit: 20}) makePage({limit: 20}) makePage({limit: 20}) makePage({limit: 20}) makePage({limit: 20}) Perfect world
  16. 16. function (property, x) { return x[property]; } Getters/Setters function (property, value, x) { x[property] = value; return x; }
  17. 17. Lenses over(l, f, x) view(l, x) set(l, y, x)
  18. 18. var user = {id: 1, name: ‘Alicia'} var L = makeLenses([‘name’]) view(L.name, user) // 'Alicia' set(L.name, 'Ally', user) // {id: 1, name: 'Ally'} over(L.name, toUpperCase, user) // {id: 1, name: 'ALICIA'} Lenses
  19. 19. var user = {id: 1, name: {first: ‘doris’, last: ‘day’ }} var L = makeLenses([‘name’, ‘first’]) var firstNameChar = compose(L.name, L.first, _1) over(firstNameChar, toUpperCase, user) //=> {id: 1, name: {first: ‘Doris’, last: ‘day’ }} Lenses
  20. 20. view(l, set(l, b, a)) == b set(l, view(l, a), a) == a set(l, c, set(l, b, a)) == set(l, c, a) Lens laws
  21. 21. if(x !== null && x !== undefined) { return f(x) } Null checking
  22. 22. fmap(f, Maybe(x)) Null checking
  23. 23. var fmap = function(f, mappable) { return mappable.map(f) } Null checking
  24. 24. fmap(function(x) { return x.toUpperCase() }, Maybe(‘hi’)) //=> Maybe(‘HI’) fmap(function(x) { return toUpperCase(x); }, Maybe(null)) //=> Maybe(null) Null checking
  25. 25. fmap(function(x) { return x.toUpperCase() }, Maybe(‘hi’)) //=> Maybe(‘HI’) fmap(function(x) { return x.toUpperCase() }, Maybe(null)) //=> Maybe(null) Null checking
  26. 26. compose(fmap(f), Maybe) Null checking
  27. 27. var id = function(x) { return x; } fmap(id, x) == id(x) Fmap laws (identity)
  28. 28. compose(fmap(f), fmap(g)) == fmap(compose(f, g)) Fmap laws (composition)
  29. 29. if(x !== null && x !== undefined) { return f(x) } else { throw ‘Some Error!’ } Error Handling
  30. 30. Error Handling fmap(f, Either(‘Some error’, x))
  31. 31. Either(‘need an int’, 3) //=> Right(3) fmap(function(x) { return x + 1; }, Either(‘need an int’, undefined)) //=> Left(‘need an int’) Error Handling
  32. 32. Either(‘need an int’, 3) //=> Right(3) Either(‘need an int’, undefined)) //=> Left(‘need an int’) Error Handling
  33. 33. fmap(function(x) { return x + 1; }, Right(2)) //=> Right(3) fmap(function(x) { return x + 1; }, Either(‘need an int’, undefined)) //=> Left(‘need an int’) Error Handling
  34. 34. fmap(function(x) { return x + 1; }, Right(2)) //=> Right(3) fmap(function(x) { return x + 1; }, Left(‘need an int’)) //=> Left(‘need an int’) Error Handling
  35. 35. compose(fmap(f), Either(‘error’)) Error Handling
  36. 36. f(x, function(y) { return g(y); }); Future values
  37. 37. Future values fmap(f, Promise(x))
  38. 38. var p = new Promise(); fmap(function(x) { return log(reverse(x)) }, p) //=> Promise() p.resolve([1,2,3]) //=>[3, 2, 1] Future values
  39. 39. Something that implements map Functor
  40. 40. if(x !== null && x !== undefined) { var y = f(x) if(y !== null && y !== undefined) { return g(y) } } Nesting
  41. 41. f(x, function(y) { return g(y, function(z) { return h(z) }) }) Nesting
  42. 42. compose(mjoin, fmap(f)) Nesting
  43. 43. var getField = compose(Maybe, document.querySelector) var getValue = compose(Maybe, pluck(‘value’)) var greet = compose(fmap(fmap(concat(‘hello’))), fmap(getValue), getField) greet(‘#name’) //=> Maybe(Maybe(‘hello chris’)) var greet = compose(fmap(concat(‘hello’)), mjoin, fmap(getValue), getField) greet(‘#name’) //=> Maybe(‘hello chris’) Nesting
  44. 44. var getField = compose(Maybe, document.querySelector) var getValue = compose(Maybe, pluck(‘value’)) var greet = compose(fmap(fmap(concat(‘hello’))), fmap(getValue), getField) greet(‘#name’) //=> Maybe(Maybe(‘hello chris’)) var greet = compose(fmap(concat(‘hello’)), mjoin, fmap(getValue), getField) greet(‘#name’) //=> Maybe(‘hello chris’) Nesting
  45. 45. compose(mjoin, fmap(g), mjoin, fmap(f)) Nesting mcompose(g, f)
  46. 46. compose(mjoin, fmap(g), mjoin, fmap(f)) Nesting mcompose(g, f)
  47. 47. mcompose(mcompose(f, g), h) == mcompose(f, mcompose(g, h)) mcompose(f, M) == f mcompose(M, f) == f Monad laws
  48. 48. Multiple null args var notNull = function(x) { return x !== null && x !== undefined } if(notNull(x) && notNull(y)) { return f(x, y) }
  49. 49. Multiple Async fn’s var y,z; f(x, function(result) { y = result; if(z) { return h(y, z) }) }) g(x, function(result) { z = result; if(y) { return h(y, z) }) })
  50. 50. liftA2(f, A(x), A(y)) Multiple values
  51. 51. liftA3(f, A(x), A(y), A(z)) Multiple values
  52. 52. liftA2(add, Maybe(3), Maybe(4)) //=> Maybe(7) liftA2(add, Maybe(null), Maybe(4)) //=> Maybe(null) Multiple values
  53. 53. liftA2(add, Maybe(3), Maybe(4)) //=> Maybe(7) liftA2(add, Maybe(null), Maybe(4)) //=> Maybe(null) Multiple values
  54. 54. var tweets_p = Http.get(‘/twitter/tweets’) var photos_p = Http.get(‘/flickr/photos’) var makeCollage = _.curry(function (tweets, photos){}) liftA2(makeCollage, tweets_p, photos_p) Multiple values
  55. 55. // identity ap(A(id), m) == m // composition ap(ap(ap(A(compose), f), g), w) == ap(f, ap(g, w)) // homomorphism ap(A(f), A(x)) == A(f(x)) // interchange ap(u, A(x)) == ap(A(function(f) { return f(x); }), u) Applicative laws
  56. 56. Accumulation reduce(function(acc, x) { return acc + x; }, 0, [1,2,3])
  57. 57. Accumulation reduce(function(acc, x) { return acc * x; }, 1, [1,2,3])
  58. 58. Accumulation reduce(function(acc, x) { return acc || x; }, false, [false, false, true])
  59. 59. Accumulation reduce(function(acc, x) { return acc && x; }, true, [false, false, true])
  60. 60. Accumulation reduce(function(acc, x) { return acc > x ? acc : x; }, 0, [12, 5, 35])
  61. 61. Monoid mappend(m, m) mempty(m) mconcat([m])
  62. 62. Monoid mappend(m, m) mempty(m) mconcat([m])
  63. 63. mconcat([Sum(1), Sum(2), Sum(3)]) //=> Sum(6) Accumulation
  64. 64. mconcat([Product(1), Product(2), Product(3)]) //=> Product(6) Accumulation
  65. 65. mconcat([Max(13), Max(2), Max(9)]) //=> Max(13) Accumulation
  66. 66. mconcat([Any(false), Any(false), Any(true)]) //=> Any(true) Accumulation
  67. 67. mconcat([All(false), All(false), All(true)]) //=> All(false) Accumulation
  68. 68. compose(mconcat, map(M)) Accumulation
  69. 69. // left identity mappend(mempty, x) == x // right identity mappend(x, mempty) == x // associativity mappend(mappend(x, y), z) == mappend(x, mappend(y, z)) Monoid laws
  70. 70. Combinators function(x) { return [f(x), g(x)] }
  71. 71. Combinators function(x, y) { return [f(x), g(y)] }
  72. 72. compose(f, g) ampersand(f, g) asterisk(f, g) first(f) second(f) Arrows
  73. 73. first(reverse)([‘Stan’, ‘Lee']) // [‘natS’, ‘Lee’] second(reverse)([‘Stan’, ‘Lee']) // [‘Stan’, ‘eeL’] ampersand(reverse, toUpperCase)(‘Stan’) // [‘natS’, ‘STAN’] asterisk(reverse, toUpperCase)([‘Stan’, ‘Lee']) // [‘natS’, ‘LEE’] Arrows
  74. 74. A(id) == id A(compose(f, g)) == A(compose(f, A(g))) first(A(f)) == A(first(f)) first(compose(f, g)) == compose(first(f), first(g)) compose(first(f), A(pluck(0))) == compose(A(pluck(0)), f) compose(first(f), A(asterisk(id, g)) == compose(A(asterisk(id, g)), first(f)) compose(first(first(f)), A(assoc) == compose(A(assoc), first(f)) Arrow laws
  75. 75. Thanks! @drboolean https://github.com/DrBoolean/patterns_talk

×