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

FKit: Everyday Functional Programming in JavaScript

Josh Bassett
October 08, 2014

FKit: Everyday Functional Programming in JavaScript

In this talk I present a functional programming library I've been working on recently: FKit.

https://github.com/nullobject/fkit

Josh Bassett

October 08, 2014
Tweet

More Decks by Josh Bassett

Other Decks in Programming

Transcript

  1. Curried Functions - Like regular functions, but more delicious -

    A curried function is a function that instead of taking multiple arguments takes exactly one argument. - It returns another function that takes exactly one argument, and so on. - When all the arguments are specified the result is returned.
  2. Curried Functions function add(a, b) { return a + b;

    }
 function add(a) {
 return function(b) {
 return a + b;
 }
 }
 add(1); // function add(1)(2); // 3
  3. Partially Applied Functions - What happens if we don't specify

    all the arguments to a curried function? - Instead of a result, we get a partially applied function.
  4. Partially Applied Functions function mul(a) {
 return function(b) {
 return

    a * b;
 }
 }
 var double = mul(2);
 double(1); // 2
 double(2); // 4
 double(3); // 6
  5. Function Composition - Function composition is a powerful way to

    specify a series of function applications. - Each function takes as an argument the result of the inner function. - Mathematically it is expressed: f . g 㱻 f(g(a))
  6. Function Composition function compose(f, g) {
 return function(a) {
 return

    f(g(a));
 };
 }
 var f = compose(add(1), mul(2));
 f(1); // 3
 f(2); // 5
 f(3); // 7
  7. FKIT - Provides many functions for solving common problems with

    functions, objects, arrays, and strings. - Related to underscore, lodash, etc, but much more focussed on everyday utility. - Most functions are curried by default - Order of arguments to functions is natural. - Small: about 3 KB minified and gzipped!
  8. Numbers F.add(1, 2); // 3
 F.add(1)(2); // 3
 F.sub(1, 2);

    // 1
 F.sub(1)(2); // 1
 F.max(1, 2); // 2
 F.max(1)(2); // 2
 F.inc(1); // 2
 F.dec(2); // 1
  9. Predicates F.eq(1, 2); // false
 F.eq(2, 2); // true
 F.gt(1,

    2); // true
 F.gt(2, 1); // false
 var p = F.gt(1);
 p(1); // false
 p(2); // true
 [1, 2, 3].filter(F.gt(1)); // [2, 3]
  10. Predicates var p = F.whereAll([F.gt(1), F.lt(3)]);
 p(1); // false
 p(2);

    // true
 p(3); // false
 F.all(F.gt(1), [1, 2, 3]); // false
 F.any(F.gt(1), [1, 2, 3]); // true
  11. Lists - Both strings and arrays are treated as lists.

    - This means you can use list combinators on both strings and arrays. - e.g. map, filter, fold, scan, zip, and many more.
  12. Lists - Yes, strings are lists! - “abc” => [‘a’,

    ‘b’, ‘c’] - [‘a’, ‘b’, ‘c’] => “abc”
  13. Lists [1, 2, 3].reverse(); // [3, 2, 1]
 'hola'.reverse();
 TypeError:

    Object hola has no method ‘reverse’.
 F.reverse([1, 2, 3]); // [3, 2, 1]
 F.reverse('hola'); // 'aloh'
  14. Basics F.head([1, 2, 3]); // 1
 F.head('foo'); // 'f'
 F.tail([1,

    2, 3]); // [2, 3]
 F.tail('foo'); // 'oo'
 F.inits([1, 2, 3]); // [[], [1], [1, 2], [1, 2, 3]]
 F.inits('foo'); // ['', 'f', 'fo', 'foo']
 F.tails([1, 2, 3]); // [[1, 2, 3], [2, 3], [3], []]
 F.tails('foo'); // ['foo', 'oo', 'o', '']
  15. Transformations F.prepend(1, [2, 3]); // [1, 2, 3]
 F.prepend('f', 'oo');

    // 'foo'
 F.append(3, [1, 2]); // [1, 2, 3]
 F.append('o', 'fo'); // 'foo'
 F.concat([1], [2, 3], [4, 5, 6]); // [1, 2, 3, 4, 5, 6]
 F.concat('f', 'oo', 'bar'); // 'foobar'
  16. Transformations F.intersperse(4, [1, 2, 3]); // [1, 4, 2, 4,

    3]
 F.intersperse('-', 'hola'); // 'h-o-l-a'
 F.surround(0, 4, [1, 2, 3]); // [0, 1, 2, 3, 4]
 F.surround('¡', '!', 'hola'); // '¡hola!'
  17. Combinators F.map(F.inc, [1, 2, 3]); // [2, 3, 4]
 F.map(F.toUpper,

    'foo'); // ['F', 'O', 'O']
 F.fold(F.add, 0, [1, 2, 3]); // 6
 F.fold(F.flip(F.prepend), '', 'foo'); // 'oof'
 F.filter(F.eq(2), [1, 2, 3]); // [2]
 F.filter(F.eq('o'), 'foo'); // 'oo'
  18. Combinators F.scan(F.add, 0, [1, 2, 3]);
 // [0, 1, 3,

    6]
 F.scan(F.flip(F.prepend), 'foo');
 // ['', 'f', 'of', 'oof']
 F.zip([1, 2, 3], [4, 5, 6]);
 // [[1, 4], [2, 5], [3, 6]]
 F.zip('foo', 'bar');
 // [['f', 'b'], ['o', 'a'], ['o', 'r']]
  19. Sublists F.take(2, [1, 2, 3]); // [1, 2]
 F.take(2, 'foo');

    // 'fo'
 F.drop(2, [1, 2, 3]); // [3]
 F.drop(2, 'foo'); // 'o'
 F.split(F.eq(1), [1, 2, 3]); // [[1], [2, 3]]
 F.split(F.eq('a'), 'abc')); // ['a', 'bc']
 F.group([1, 2, 2, 3, 3, 3]);
 // [[1], [2, 2], [3, 3, 3]]
 F.group('Mississippi');
 // ['M', 'i', 'ss', 'i', 'ss', 'i', 'pp', 'i']
  20. Sets F.nub([1, 2, 2, 3, 3, 3]); // [1, 2,

    3]
 F.nub('abbccc'); // 'abc'
 F.union([1, 2, 3], [2, 3, 4]); // [1, 2, 3, 4]
 F.union('abc', 'bcd'); // 'abcd'
 F.intersect([1, 2, 3], [2, 3, 4]); // [2, 3]
 F.intersect('abc', 'bcd'); // 'bc'
 F.cartesian([1, 2], [3, 4]);
 // [[1, 3], [1, 4], [2, 3], [2, 4]]
 F.cartesian('ab', 'cd');
 // [['a', 'c'], ['a', 'd'], ['b', 'c'], ['b', 'd']]
  21. Objects var shape = {type: 'circle', color: 'red'};
 F.get('color', shape);

    // 'red'
 F.set('color', 'green', shape);
 // {type: 'circle', color: 'green'}
 F.copy(shape, {color: 'blue', size: 1});
 // {type: 'circle', color: 'blue', size: 1}
  22. Objects var shapes = [
 {type: 'circle', color: 'red', size:

    1},
 {type: 'square', color: 'green', size: 2},
 {type: 'triangle', color: 'blue', size: 3}
 ];
 shapes.map(F.get('color'));
 // ['red', 'green', 'blue'];
 shapes.map(F.set('size', 100));
 // [{…, size: 100}, {…, size: 100}, {…, size: 100}]
  23. Shootout #1 Calculate the maximum number in a list. i.e.

    Given a list of as, find the largest a.
  24. Shootout #1 var as = [1, 2, 3];
 as.reduce(Math.max.apply(Math, as));

    // 3
 Math.max.apply(null, as); // 3
 F.fold(F.max, as[0], as); // 3
 F.maximum(as); // 3
  25. Shootout #2 Calculate the sums of the corresponding numbers in

    two lists. The lists are not guaranteed to have the same length. i.e. Given lists of as and bs, for each pair [a, b] calculate a + b.
  26. Shootout #2 var as = [1, 2, 3], bs =

    [4, 5, 6, 7];
 var cs = [];
 for (var i = 0; i < Math.min(as.length, bs.length); i++) {
 cs.push(as[i] + bs[i]);
 }
 cs; // [5, 7, 9]
 F.zipWith(F.sum, as, bs); // [5, 7, 9]
  27. Shootout #3 Calculate the union of two lists. i.e. Given

    lists of as and bs, calculate a list of cs such that it only contains distinct elements from as and bs.
  28. Shootout #3 var as = [1, 2, 3], bs =

    [2, 3, 4];
 var cs = [];
 as.reduce(function(cs, a) {
 if (cs.indexOf(a) < 0) { cs.push(a); }
 return cs;
 }, cs);
 bs.reduce(function(cs, b) {
 if (cs.indexOf(b) < 0) { cs.push(b); }
 return cs;
 }, cs);
 cs; // [1, 2, 3, 4]
 F.union(as, bs); // [1, 2, 3, 4]
  29. Shootout #4 Find the longest run of numbers in a

    list. i.e. Given a list of as, calculate the longest contiguous sequence of equal elements.
  30. Shootout #4 var as = [1, 2, 2, 3, 3,

    3];
 as.reduce(function(bs, a) {
 if (bs[0] !== a) { bs = [a]; } else { bs.push(a); }
 return bs;
 }, []).length // 3;
 F.compose(
 F.maximum,
 F.map(F.length),
 F.group
 )(as); // 3