Slide 1

Slide 1 text

FKIT: Everyday Functional Programming Josh Bassett in JavaScript

Slide 2

Slide 2 text

A Couple of Things…

Slide 3

Slide 3 text

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.

Slide 4

Slide 4 text

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

Slide 5

Slide 5 text

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.

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

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

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

A Functional Programming Toolkit

Slide 10

Slide 10 text

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!

Slide 11

Slide 11 text

Numbers - FKit provides curried versions of most built-in numerical operators and functions.

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

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]

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

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.

Slide 16

Slide 16 text

Lists - Yes, strings are lists! - “abc” => [‘a’, ‘b’, ‘c’] - [‘a’, ‘b’, ‘c’] => “abc”

Slide 17

Slide 17 text

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'

Slide 18

Slide 18 text

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', '']

Slide 19

Slide 19 text

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'

Slide 20

Slide 20 text

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!'

Slide 21

Slide 21 text

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'

Slide 22

Slide 22 text

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']]

Slide 23

Slide 23 text

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']

Slide 24

Slide 24 text

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']]

Slide 25

Slide 25 text

Objects - FKit provides functions for handling immutable objects.

Slide 26

Slide 26 text

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}

Slide 27

Slide 27 text

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}]

Slide 28

Slide 28 text

Shootout JavaScript vs. FKit

Slide 29

Slide 29 text

Shootout #1 Calculate the maximum number in a list. i.e. Given a list of as, find the largest a.

Slide 30

Slide 30 text

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

Slide 31

Slide 31 text

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.

Slide 32

Slide 32 text

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]

Slide 33

Slide 33 text

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.

Slide 34

Slide 34 text

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]

Slide 35

Slide 35 text

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.

Slide 36

Slide 36 text

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

Slide 37

Slide 37 text

Docs & Examples

Slide 38

Slide 38 text

github.com/nullobject/fkit @nullobject