Functional Programming in Javascript

Jesús García

Agenda

  • Why FP
    • Purity
    • Immutability
    • High level
  • Tacit programming
    • Currying
    • Composition
  • Algebraic Data Types
    • Functors
    • Monoids
    • Monads
    • Lenses

Why Functional Programming

// impure

const add = (x, y) => {
  launchMissiles()
  return x + y
}

// pure

const add = (x, y) => x + y

Purity

const accounts = [{
  amount: 10,
  currency: 'USD'
}, {
  amount: 20,
  currency: 'EUR'
}]

const newAccount = {
  amount: 3,
  currency: 'MXN'
}


// mutable

accounts.push(newAccount)

//immutable

newAccounts = accounts.concat(newAccount)

Immutability

High level

Low level

// imperative

var accounts = [{
    amount: 1234,
    currency: EUR
  }, {
    amount: 6578,
    currency: EUR
  }, {
    amount: 6234,
    currency: USD
  }
]

var totalEur = 0

for (var i = 0; i < accounts.length; i++) {
  if (account.currency === 'EUR'){
    totalEur += account.amount  
  }
}

High level

// functional

accounts
  .filter(account => 
    account.currency === 'EUR')
  .reduce((total, account) => 
    total + account.amount)


// functional (refactor)

const add = (x, y) => x + y

const prop = prop => obj => obj[prop]

const propEq = (prop, value) => obj => obj[prop] === value

accounts
 .filter(propEq('currency', currency)
 .map(prop('amount'))
 .reduce(add, 0)

Performance

Lazyness

Tacit programming

Tacit programming, also called point-free style, is a programming paradigm in which function definitions do not identify the arguments (or "points") on which they operate.

Wikipedia

Currying

// aplicación parcial

var add = function(a) {
  return function(b) {
    return a + b
  }
}

// para funciones binarias se puede hacer:

var add = function(a, b) {
  if (b === undefined) {
    return function(b) {
      return a + b
    }
  }
  return a + b
}

// con la función 'curry'

var add = curry(function(a, b) {
  return a + b
})

// con arrow functions

const add = a => b => a + b

ES6 Arrow functions

// implicit return 

const shout = string => string.toUppercase() + '!!!'


// no implicit return

const foo = (arg1, arg2) => {
  // do stuff
  // return something
}

// return an object by wrapping it in parentheses

const makeCar = wheels => ({
  wheels: wheels
})

// wrap any expression in parenthesis for implicit return

const sanitize = string => (
  string
    .trim()
    .toUpperCase()
)

Composition

//(binary) compose

const compose = f => g => x => f(g(x))

const upperCase = s => s.toUpperCase()

const trim = s => s.trim()

const sanitize = compose(
  upperCase,
  trim
)

//pipe is a variadic compose 
//that goes from left to right

const pipe = (...fns) => x => fns.reduce((acc, fn) => fn(acc), x)

const inc = x => x + 1
const square = x => x * x
const timesTen = x => x * 10

const operations = pipe(
  inc,
  square,
  timesTen
)

const arr = [1,2,3]

arr.map(operations) //[40, 90, 160]

Functors

const Id = v => 
({
  value: v,
  map: f => Id(f(v))

})

//Array is a functor

['hello']
    .map(str => str.length)
    .map(n => n < 5)
    [0]

//map delegates to the implementation 
//of the specific functor

const map = fn => functor =>
    functor.map(fn)

Functor Laws

u.map(a => a) is equivalent to u (identity)
u.map(x => f(g(x))) is equivalent to u.map(g).map(f) (composition)


map :: Functor f => f a ~> (a -> b) -> f b

Monoids

const Sum = x => 
({
  x
  , concat: ({x: y}) =>
    Sum(x + y)
  , empty: _ => Sum(0)

})

const Product = x => 
({
  x
  , concat: ({x: y}) =>
    Product(x * y)
  , empty: _ => Sum(1)

})

Monoid laws

a.concat(b).concat(c) is equivalent to a.concat(b.concat(c)) (associativity)

concat :: Monoid a => a ~> a -> a


m.concat(M.empty()) is equivalent to m (right identity)
M.empty().concat(m) is equivalent to m (left identity)

empty :: Monoid m => () -> m

Monads

Lenses

var xLens = lens(prop('x'), assoc('x'));

view(xLens, {x: 1, y: 2});            //=> 1

set(xLens, 4, {x: 1, y: 2});          //=> {x: 4, y: 2}

over(xLens, R.negate, {x: 1, y: 2});  //=> {x: -1, y: 2}

Bonus Round

// refactor pipe with combinators

const apply = (f, x) => f(x)

const flip = f => (a, b) => f(b, a)

const pipe = (...fns) => x => fns.reduce(flip(apply), x)

FP in JS

By Jesús García Martínez