720

# 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

October 08, 2014

## Transcript

3. ### 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 speciﬁed the result is returned.
4. ### 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
5. ### 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.
6. ### 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
7. ### 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))
8. ### 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

10. ### 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 miniﬁed and gzipped!
11. ### Numbers - FKit provides curried versions of most built-in numerical

operators and functions.
12. ### 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
13. ### 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]
14. ### 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
15. ### Lists - Both strings and arrays are treated as lists.

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

‘b’, ‘c’] - [‘a’, ‘b’, ‘c’] => “abc”
17. ### 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'
18. ### 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, 2], [1, 2, 3]]  F.inits('foo'); // ['', 'f', 'fo', 'foo']  F.tails([1, 2, 3]); // [[1, 2, 3], [2, 3], , []]  F.tails('foo'); // ['foo', 'oo', 'o', '']
19. ### 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(, [2, 3], [4, 5, 6]); // [1, 2, 3, 4, 5, 6]  F.concat('f', 'oo', 'bar'); // 'foobar'
20. ### 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!'
21. ### 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]); //   F.filter(F.eq('o'), 'foo'); // 'oo'
22. ### 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']]
23. ### Sublists F.take(2, [1, 2, 3]); // [1, 2]  F.take(2, 'foo');

// 'fo'  F.drop(2, [1, 2, 3]); //   F.drop(2, 'foo'); // 'o'  F.split(F.eq(1), [1, 2, 3]); // [, [2, 3]]  F.split(F.eq('a'), 'abc')); // ['a', 'bc']  F.group([1, 2, 2, 3, 3, 3]);  // [, [2, 2], [3, 3, 3]]  F.group('Mississippi');  // ['M', 'i', 'ss', 'i', 'ss', 'i', 'pp', 'i']
24. ### 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']]

26. ### 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}
27. ### 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}]

29. ### Shootout #1 Calculate the maximum number in a list. i.e.

Given a list of as, ﬁnd the largest a.
30. ### 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, as); // 3  F.maximum(as); // 3
31. ### 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.
32. ### 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]
33. ### 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.
34. ### 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]
35. ### 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.
36. ### Shootout #4 var as = [1, 2, 2, 3, 3,

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