Skip to content

Latest commit

 

History

History
774 lines (494 loc) · 13.7 KB

javascript-combinators.md

File metadata and controls

774 lines (494 loc) · 13.7 KB

original

^ https://www.flickr.com/photos/andy_li/4378437185

^ "A Unified Theory of JavaScript Style, Part I"


A Unified Theory of JavaScript Style, Part I

JavaScript Combinators

^ https://www.flickr.com/photos/andy_li/4378437185

^ The "backbone" of this talk is to explain the combinators make software that is easier to factor and refactor by creating an "aglgebra" where functions form a semigroup, rather than using code, which is more flexibible but creates a sparse group


^ https://www.flickr.com/photos/popculturegeek/5991539415

^ "talk about Combinators and decorators"


we'll talk about

Combinators and decorators

^ https://www.flickr.com/photos/popculturegeek/5991539415


^ https://www.flickr.com/photos/creative_stock/3521504068

^ "think about flexibility and decluttering"


but think about

Flexibility and decluttering

^ https://www.flickr.com/photos/creative_stock/3521504068


original

^ https://www.flickr.com/photos/scelera/3036797409

^ "We compose entities to create new entities"


composition

We compose entities to create new entities

^ https://www.flickr.com/photos/scelera/3036797409


original

^ https://www.flickr.com/photos/justinbaeder/5317820857

^ Not all entities "fit together"


interfaces

Not all entities "fit together"

^ https://www.flickr.com/photos/justinbaeder/5317820857

^ worse, some are time-dependent


original

^ https://www.flickr.com/photos/michale/2744016741

^ "Homogeneous interfaces create dense spaces"


Homogeneous interfaces create dense spaces

^ https://www.flickr.com/photos/michale/2744016741

^ Example: Integers and addition, multiplication: very dense


original

^ https://www.flickr.com/photos/stretta/5572576057

^ "Heterogeneous interfaces create sparse spaces"


Heterogeneous interfaces create sparse spaces

^ https://www.flickr.com/photos/stretta/5572576057

^ counter-example: imagine an operation on integers wher emost operations were invalid. you'd have to engage in a complex search to find the path from any one integer to another.


original

^ https://www.flickr.com/photos/michale/2744016741

original

^ https://www.flickr.com/photos/stretta/5572576057

^ "Dense is more flexible than sparse"


Dense is more flexible than sparse

^ https://www.flickr.com/photos/michale/2744016741

^ https://www.flickr.com/photos/stretta/5572576057


original

^ https://www.flickr.com/photos/dipfan/110857520

original

^ https://www.flickr.com/photos/nathanoliverphotography/9604185186

^ "Sparse can be quicker to grasp"


Sparse can be quicker to grasp

^ https://www.flickr.com/photos/dipfan/110857520

^ https://www.flickr.com/photos/nathanoliverphotography/9604185186

^ we'll give an example, showing how we have to build up.


original

^ https://www.flickr.com/photos/joao_trindade/4362414729

^ "enough with the math!"


enough with the math!

^ https://www.flickr.com/photos/joao_trindade/4362414729


original

^ https://www.flickr.com/photos/mediterraneaaan/12756787045

^ "pluck"


pluck: "A convenient version of what is perhaps the most common use-case for map: extracting a list of property values."

^ https://www.flickr.com/photos/mediterraneaaan/12756787045

^ underscorejs.org


"pluckWith" is the flipped form of "pluck"


function pluck (mappable, key) {
  return mappable.map(function (obj) {
		return obj[key];
	});
};

function pluckWith (key, mappable) {
  return pluck(mappable, key);
};

var stooges = [
	{name: 'moe', age: 40},
	{name: 'larry', age: 50},
	{name: 'curly', age: 60}];

pluckWith('name', stooges);
	//=> ["moe", "larry", "curly"]

Let's make "pluckWith" out of combinators


A unary combinator

function flip (fn) {
	return function flipped (a, b) {
		return fn.call(this, b, a);
	}
}

function arrow (a, b) {
    return "" + a + " -> " + b;
}

flip(arrow)("x", "y")
	//=> 'y -> x'

^ http://jsfiddle.net/raganwald/wFRP8/


^ https://www.flickr.com/photos/saramarlowe/8170948596

^ Another unary combinator


curry

Another unary combinator

^ https://www.flickr.com/photos/saramarlowe/8170948596


function curry (fn) {
	return function curried (a, optionalB) {
		if (arguments.length > 1) {
			return fn.call(this, a, optionalB);
		}
		else return function partiallyApplied (b) {
			return fn.call(this, a, b);
		}
	}
}

^ binary only


Currying:

var curriedArrow = curry(arrow);
	//=> [function]
	
curriedArrow('finger')('moon')
	//=> 'finger -> moon'

^ http://raganwald.com/2013/03/07/currying-and-partial-application.html


Partial Application:

var taoism = curry(arrow)('finger');
	//=> [function]
	
taoism('moon')
	//=> 'finger -> moon'

^ http://raganwald.com/2013/03/07/currying-and-partial-application.html


^ https://www.flickr.com/photos/genista/4449316

^ "Partial application transforms binary operations into unary operations"


nota bene

Partial application transforms binary operations into unary operations

^ https://www.flickr.com/photos/genista/4449316

^ makes the interface more homogeneous


function get (object, property) {
	return object[property];
}

get({foo: 1}, 'foo')
	//=> 1

var getWith = curry(flip(get));

getWith('foo')({foo: 1})
	//=> 1

function map (mappable, fn) {
	return mappable.map(fn, this);
}

function double (n) {
    return n * 2;
}

map([1, 2, 3], double)
	//=> [2, 4, 6]

var mapWith = curry(flip(map));

mapWith(double, [1, 2, 3]);
	//=> [2, 4, 6]

var doubleAll = mapWith(double);

doubleAll([1, 2, 3])
	//=> [2, 4, 6]

almost there...


function pluckWith (attr) {
  return mapWith(getWith(attr));
}

^ https://www.flickr.com/photos/ctaweb/8487304182

^ "Compose"


Compose

^ https://www.flickr.com/photos/ctaweb/8487304182


function compose (a, b) {
	return function composed (c) {
		return a(b(c));
	}
}

quod erat demonstrandum

The combinator implementation of "pluckWith"


var pluckWith = compose(mapWith, getWith);

Let's compare both implementations of "pluckWith"


var pluckWith = compose(mapWith, getWith);

//// versus ////

function pluck (mappable, key) {
  return mappable.map(function (obj) {
		return obj[key];
	});
};

function pluckWith (key, mappable) {
  return pluck(mappable, key);
};

lesson

Composing functions with combinators increases code flexibility...

^ teases apart heterogeneous factoring


lesson

Composing functions with combinators demands increased mental flexibility

^ asterix: with care, they can be easier to read.


using combinators to make

Decorators


function Cake () {}

extend(Cake.prototype, {
  mix: function () {
    // mix ingredients together
		return this;
  },
  rise: function (duration) {
    // let the ingredients rise
		return this;
  },
  bake: function () {
    // do some baking
		return this;
  }
});

fluent

function fluent (methodBody) {
  return function fluentized () {
    methodBody.apply(this, arguments);
    return this;
  }
}

function Cake () {}

extend(Cake.prototype, {
  mix: fluent( function () {
    // mix ingredients together
  }),
  rise: fluent( function (duration) {
    // let the ingredients rise
  }),
  bake: fluent(function () {
    // do some baking
  })
});

new requirements

Mix before rising or baking


extend(Cake.prototype, {
  mix: fluent( function () {
    // mix ingredients together
  }),
  rise: fluent( function (duration) {
		this.mix();
    // let the ingredients rise
  }),
  bake: fluent(function () {
		this.mix();
    // do some baking
  })
});

before

a combinator that transforms decorations into decorators

var before = curry(
	function decorate (decoration, method) {
		return function decoratedWithBefore () {
    	decoration.apply(this, arguments);
    	return method.apply(this, arguments);
		};
  }
);

var mixFirst = before(function () {
  this.mix()
});

the final version

extend(Cake.prototype, {
  
  // Other methods...
  
  mix: fluent( function () {
    // mix ingredients together
  }),
  rise: fluent( mixFirst( function (duration) {
    // let the ingredients rise
  })),
  bake: fluent( mixFirst( function () {
    // do some baking
  }))
});

lesson

Decorators declutter secondary concerns


after

var after = curry(
	function decorate (decoration, method) {
		return function decoratedWithAfter () {
			var returnValue = method.apply(this, arguments);
    	decoration.apply(this, arguments);
    	return returnValue;
		};
  }
);

around

var around = curry(
	function decorate (decoration, method) {
		return function decoratedWithAround () {
			var methodPrepended = [method].concat(
				[].slice.call(arguments, 0)
			);
			
    	return decoration.apply(this, methodPrepended);
		};
  }
);

call me maybe

var maybe = around(function (fn, value) {
	if (value != null) {
		return fn.call(this, value);;
	}
});

maybe(double)(2)
	//=> 4
	
maybe(double)(null)
	//=> undefined

generalized guards

function provided (guard) {
	return around(function () {
		var fn = arguments[0],
		    values = [].slice.call(arguments, 1);
				
		if (guard.apply(this, values)) {
			return fn.apply(this, values);
		}
	});
}

var maybe = provided( function (value) { 
	return value != null; 
});

inversions

function not (fn) {
	return function notted () {
		return !fn.apply(this, arguments)
	}
}

var except = compose(provided, not);

var maybe = except( function (value) {
	return value == null;
});

original

^ https://www.flickr.com/photos/68112440@N07/6210847796

^ "lessons"


lessons

^ https://www.flickr.com/photos/68112440@N07/6210847796


original

^ https://www.flickr.com/photos/paulmccoubrie/6792412657

^ "Combinators increase code flexibility and require increased mental flexibility"


lesson one

Combinators increase code flexibility and require increased mental flexibility

^ https://www.flickr.com/photos/paulmccoubrie/6792412657


original

^ https://www.flickr.com/photos/terry_wha/107810852

^ "Decorators declutter secondary concerns"


lesson two

Decorators declutter secondary concerns

^ https://www.flickr.com/photos/terry_wha/107810852


original

^ https://www.flickr.com/photos/suburbanbloke/723665503

^ "Do not follow in the footsteps of the sages. Seek what they sought"


lesson three

Do not follow in the footsteps of the sages.

Seek what they sought

^ https://www.flickr.com/photos/suburbanbloke/723665503


Reginald Braithwaite

GitHub, Inc.

raganwald.com

@raganwald

NDC Conference, Oslo, Norway, June 5, 2014

right, fit

^ https://leanpub.com/javascript-allonge