Functional Programming

in Javascript

With

LODASH

ED ATRERO

@edatrero

github.com/eatrero

Functional programming (lite)

  • Using lots of small functions
  • Easier to test

Example

violations = [
	{
		"uuid" : "916a7bb5-fc2a-4edf-a365-32d599d56366",
		"id" : "box1",
		"type" : "TOO_LARGE"
	},
	{
		"uuid" : "b3f25844-e5f7-4b60-915e-bd983a3deaed",
		"id" : "box1",
		"type" : "TOO_LARGE"
	},
	{
		"uuid" : "916a7bb5-fc2a-4edf-a365-32d599d56366",
		"id" : "box4",
		"type" : "TOO_LARGE"
	},
	{
		"uuid" : "916a7bb5-fc2a-4edf-a365-32d599d56366",
		"id" : "box5",
		"type" : "TOO_SMALL"
	},
	{
		"uuid" : "916a7bb5-fc2a-4edf-a365-32d599d56366",
		"id" : "box7",
		"type" : "TOO_SMALL"
	},
	{
		"uuid" : "916a7bb5-fc2a-4edf-a365-32d599d56366",
		"id" : "box12",
		"type" : "TOO_LARGE"
	},
        { ... }
]
filterViolations(violations, 'page2', "916a7bb5-fc2a-4edf-a365-32d599d56366")

// Outputs:
// [{ boxId: 'box4', messageType: 'TOO_LARGE' },
//  { boxId: 'box5', messageType: 'TOO_SMALL' },
//  { boxId: 'box7', messageType: 'TOO_SMALL' } ]


var pageFilters = { 'page1' : ['box1', 'box2', 'box2b', 'box3'],
		    'page2' : ['box4', 'box5', 'box6', 'box7', 'box8a', 'box8b'],
		    'page3' : ['box9', 'box10', 'box11'],
		    'page4' : ['box12', 'box13'] };

var pageFilters = { 'page1' : ['box1', 'box2', 'box2b', 'box3'],
		    'page2' : ['box4', 'box5', 'box6', 'box7', 'box8a', 'box8b'],
		    'page3' : ['box9', 'box10', 'box11'],
		    'page4' : ['box12', 'box13'] };

var violations = [
	{
		"uuid" : "916a7bb5-fc2a-4edf-a365-32d599d56366",
		"id" : "box1",
		"type" : "TOO_LARGE"
	},
        { ... } ]

function filterViolationsImp(violations, pageId, uuid){
    var i, out=[];
    for(i=0; i<violations.length; i++){
	if(violations[i].uuid === uuid){
	    if(pageFilters[pageId].indexOf(violations[i].id) !== -1)
                out.push( { "boxId" : violations[i].id, "messageType" : violations[i].type });
	}
    }
    return out;
}

    

imperative programming

  • Familiar
  • Efficient

Problems with imperative

When adding additional complexity, code:

  • Gets harder to reason about
  • Gets harder to test
  • Gets harder to maintain (brittle)

Functional approach

var _ = require('lodash');

function filterViolationsChain(violations, pageId, uuid){
    return _.chain(violations)
	.filter((v) => v.uuid === uuid)
	.filter((v) => pageFilters[pageId].indexOf(v.id) !== -1)
	.map((v) => ({ "boxId": v.id, "messageType" : v.type }))
	.value();
}
function filterViolationsImp(violations, pageId, uuid) {
    var i, out=[];

    for(i=0; i<violations.length; i++){
	if(violations[i].uuid === uuid){
	    if(pageFilters[pageId].indexOf(violations[i].id) !== -1)
                out.push( { "boxId" : violations[i].id, "messageType" : violations[i].type });
	}
    }
    return out;
}

Functional approach

  • Easier to reason about
  • Easier to modify/maintain
  • Easier to test
  • Less efficient

“We should forget about small efficiencies, say about 97% of the time; premature optimization is the root of all evil” 

DONALD E. KNUTH

filterViolations(violations, ['page1','page2'], "916a7bb5-fc2a-4edf-a365-32d599d56366")
function filterViolationsImp(violations, pageIds, uuid){
  var i, j, out=[];
  for(i=0; i<violations.length; i++){
    if(violations[i].uuid === uuid){
      for(j=0; j<pageIds.length; j++){
	if(pageFilters[pageIds[j]].indexOf(violations[i].id) !== -1)
	  out.push( { "boxId" : violations[i].id, "messageType" : violations[i].type });				
      }
    }
  }
  return out;
}
function filterViolationsChain(violations, pageIds, uuid){
  return _.chain(violations)
    .filter((v) => v.uuid === uuid)
    .filter((v) => violationOnPages(v, pageIds))
    .map((v) => ({ "boxId": v.id, "messageType" : v.type }))
    .value();
}

function violationOnPages(v, pageIds){
  return _.some(pageIds, (p) => pageFilters[p].indexOf(v.id) !== -1);
}

Testing deeply nested code...

Summary

  • Functional Programming: create an assembly line of functions (_.chain)
  • Easier to reason about (.filter, .map, .some)
  • Lots of small functions leads to more testable code
  • More testable code leads to higher quality code (lessBugs === happyUsers === '$$$')

Questions?

@edatrero

github.com/eatrero

Made with Slides.com