Pragmatic Introduction to

Functional Programing

Amr Draz

Introduction

Why Functional

Problem Complexity

Functional Helps

Functional Programing

 

Functions in JS

function add (a, b) {
  return a + b
}

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

const add = (a, b) => {
  return a + b
}

const add = (a, b) => a + b

//  new Function() but we won't bother with that

Ways to declare function 


function add1(a, b) {
  return a + b
}

function add2() {
  let a = arguments[0]
  let b = arguments[1]
  return a + b
}

function add3(...args) {
  let a = args[0]
  let b = args[1]
  return a + b
}


// function has a name and length property
function pass3Args(a, b, c) {
  return pass3Args.length === arguments.length
}


// functions can be called in 3 ways
function add(a, b) {
  return a + b
}

add(1, 2)
add.call(null, 1, 2)
add.apply(null, [1, 2])

Purity

let x = 20

function add3() {
    x += 3
    return x
}






add3()
add3()

Function Transform

Solve 1 to 8 then after the next section solve to 20

map and filter

const double = a => a * 2
const isEven = a => a % 2 === 0
const isOdd = a => a % 2 !== 0
const sum = (sum, a) => sum + a
const reduceDouble = (list, a) => list.concat(double(a))
const reduceIsOdd = (list, a) => (isOdd(a) ? [...list, a] : list)

function reduce(array, fn, initialValue = array[0]) {
  let acumilator = initialValue
  array.forEach(value => (acumilator = fn(acumilator, value)))
  return acumilator
}

// map [a] => [b] of same size
console.log([1, 2, 3].map(double))

// filter [a] => [a] of less or same size
console.log([1, 2, 3].filter(isEven))

// reduce ([b], a) => a
console.log(reduce([1, 2, 3], sum, 0))

// reduceMap [a] => [a]
console.log([1, 2, 3].reduce(reduceDouble, []))

// reduceFilter [a] => [a]
console.log([1, 2, 3].reduce(reduceIsOdd, []))
let obj = { age2: 40, age: 20 }

console.log(Object.keys(obj))
console.log(
  Object.keys(obj).reduce((newObj, key) => {
    newObj[key] = obj[key] * 2
    return newObj
  }, {})
)

Immutability

const state = {
  number: 7,
  person: {
      name: "person",
      age: 25
  },
  numbers: [12, 34, 45, 82] 
}

Is this immutable

const state = {
  number: 7,
  person: {
      name: "person",
      age: 25
  },
  numbers: [12, 34, 45, 82] 
}

Object.freeze(state)

state.number = 15

Is this immutable

const state = {
  number: 7,
  person: {
      name: "person",
      age: 25
  },
  numbers: [12, 34, 45, 82] 
}

Object.freeze(state)

state.numbers[2] = 15
state.person.name[2] = "n"
delete state.person.age

Is this immutable

// arrays
let index = 100
let value = 100
let list = [1, 2, 3], list3 = []

// copy 
let list1 = list.slice()
let list2 = [...list]

console.log("copy", list)
console.log(list1)
console.log(list2)

// push 
list1.push(4)

list3 = list2.concat(value)
// or
list3 = [...list2, value]

console.log("push in", list)
console.log("list1", list1)
console.log("list2", list2)
console.log("list3", list3)

// edit at index with value
list1[index] = 3

list3 = list.map((_, idx) => idx === index ? value : _ )
// or
list3 = [...list.slice(0, index), value, ...list.slice(index + 1)]
// or
list3 = [...list]
list3 = list2[2] = value

console.log("insert at index value", list)
console.log("list1", list1)
console.log("list2", list2)
console.log("list3", list3)

// remove at index
list1.splice(index, 1)

list3 = list2.filter((_, idx) => idx === index)
// or
list3 = [...list2.slice(0, index), ...list.slice(index + 1)]
// or
list3 = [...list2]
list3 = list3[2] = 3


console.log("remove at index value", list)
console.log("list1", list1)
console.log("list2", list2)
console.log("list3", list3)

How to modify an array immutably

// object
let key = "something"
let value = 100
let obj = { id: 23 }, obj3 = {}

// copy 
let obj1 = Object.assign({}, obj)
let obj2 = {...list}

console.log("copy", obj)
console.log(obj1)
console.log(obj2)

// insert at key value 
obj1[key] = value

obj3 = Object.assign({}, obj2, { [key]: value })
// or
obj3 = {...obj2, [key]: value }

console.log("insert at key", list)
console.log("obj1", obj1)
console.log("obj2", obj2)
console.log("obj3", obj3)

// remove at key
delete obj1[key]

obj3 = {...obj2, [key]: undefined }
//or
obj3 = {...obj2}
delete obj3[key]
// or we can talk about mapping and filtering using reduce first
obj3 = Object.entries(key).reduce((final, [k, val]) => k===key?{...final, [k]: value}: final, {})


console.log("remove at key", list)
console.log("obj1", obj1)
console.log("obj2", obj2)
console.log("obj3", obj3)

How to modify an object immutably

const state = {
  number: 7,
  person: {
    name: "person",
    age: 25
  },
  numbers: [12, 34, 45, 82]
}

let newState = {
  ...state
}
newState.person = {
  ...state.person,
  age: 10
}

console.log(state, newState)

How to modify a nested object immutably

Complete Exercise

Higher-Order Functions

const add = a => b => a * b
const multiply = a => b => a * b

const isEven = a => a % 2 === 0

const negate = fn => (...args) => !fn()

// const isOdd = a => a % 2 !== 0
const isOdd = negate(isEven)
function head(x) { return x[0]; }
function split(x) { return x.split(" "); }
function doubleStr(x) { return x.repeat(2); }
function exclaim(x) { return x + "!"; }
function increment(x) { return x + 1; }
function decrement(x) { return x - 1; }
function double(x) { return x * 2; }
function half(x) { return x / 2; }



const splitHead = compose2(head, split)

// const halfenIncrementDoubleDecrement = compose(decrement,double,increment,half)

// const incrementHalfDoubleDecrement = pipe(increment, half, double, decrement)

References

Pragmatically Functional

By Amr Draz

Pragmatically Functional

  • 137