# Functional Programming

### in Javascript

### Content

- About me
- The Problem
- What is FP?
- Terminology
- Use Cases
- Standard Library
- External Modules

### About me

Julian Tölle
Developer @ narando & TrackCode
Backend Development & Devops
Javascript for 16 months

### The Problem

                getNestedValue({ foo: 1 }, 'foo') // 1
                getNestedValue({ foo: { bar: 2 } }, 'foo.bar') // 2
                getNestedValue({ hello: 'world' }, 'foo.bar') // undefined

### The Problem

                  function getNestedValue(object, propertyName) {
                    if (!propertyName) throw new Error("Impossible to set null property");

                    const parts = propertyName.split(".");
                    const len = parts.length;
                    let subObject = object;
                    let i;

                    for (i = 0; i < len; i++) {
                      if (!subObject || typeof subObject === "undefined") return undefined;
                      subObject = subObject[parts[i]];
                    }

                    return subObject;
                  }

### The Problem

                const parts = propertyName.split(".");
                const len = parts.length;
                let subObject = object;
                let i;

- multiple temporary variables
- occupying "mind-estate"

### The Problem

                  for (i = 0; i < len; i++) {
                    if (!subObject || typeof subObject === "undefined") return undefined;
                    subObject = subObject[parts[i]];
                  }

- changing state
- hard to reason about

### What is FP?

- programming paradigm
- evaluation of mathematical functions
- functions are treated like values

### Terminology

#### First-Class Functions

- treating functions the same as other values
  ( `Object`, `Number`, `String`, `Boolean`)
- function definition is allowed where a value is expected

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

                    const object = {
                      foo: 1,
                      bar: true,
                      baz: sum
                    }

### Terminology

#### Higher-Order Functions

- takes a function as a parameter || returns a function
- callbacks

                  const add3 = v => v + 3;
                  const twice = (f) => (v) => f(f(v));

                  // Call directly
                  twice(add3)(3); // 9

                  // Store as value
                  const add6 = twice(add3);
                  add6(3); // 9

                  const add12 = twice(twice)(add3);
                  add12(3); // 15

### Terminology

#### Side Effect

- mutating state outside of the scope
- modifying a global variable
- observable effect on the outside world
- order matters

                // mutating state
                let counter = 0;
                const inc = () => counter += 1;

                // observable effect
                const log = data => console.log(data);

                // Order changes the result
                const fs = require('fs');
                fs.readFileSync('./foo.txt') // "Hello World!";
                fs.writeFileSync('./foo.txt', "Bar!");
                fs.readFileSync('./foo.txt') // "Bar!"

### Terminology

#### Pure Function

- no side effects
- same parameters => same results
- examples: sha1, Math.sin,

                  const add3 = v => v + 3;

                  add3(3); // 6
                  add3(3); // 6

                  Math.sin(Math.PI / 4) // ~= 0.707
                  Math.sin(Math.PI / 4) // ~= 0.707

### Use Cases

Let's try improving the code from earlier!

                  function getNestedValue(object, propertyName) {
                    if (!propertyName) throw new Error("Impossible to set null property");

                    const parts = propertyName.split(".");
                    const len = parts.length;
                    let subObject = object;
                    let i;

                    for (i = 0; i < len; i++) {
                      if (!subObject || typeof subObject === "undefined") return undefined;
                      subObject = subObject[parts[i]];
                    }

                    return subObject;
                  }

### Use Cases

Replace the loop with `Array.prototype.forEach`

                function getNestedValue(object, propertyName) {
                  if (!propertyName) throw new Error("Impossible to set null property");

                  const parts = propertyName.split(".");
                  // const len = parts.length;
                  let subObject = object;
                  // let i;

                  parts.forEach(part => {
                    if (!subObject || typeof subObject === "undefined") return undefined;
                    subObject = subObject[part];
                  });

                  return subObject;
                }

### Use Cases

Remove the side-effect by using `Array.prototype.reduce`

                  function getNestedValue(object, propertyName) {
                    if (!propertyName) throw new Error("Impossible to set null property");

                    const parts = propertyName.split(".");
                    // let subObject = object;
                    // parts.forEach(part => {
                    //   if (!subObject || typeof subObject === "undefined") return undefined;
                    //   subObject = subObject[part];
                    // });

                    const nestedValue = parts.reduce((object, part) => {
                      if (!object || typeof object === "undefined") return undefined;
                      return object[part];
                    }, object);
                    return nestedValue;
                  }

### Use Cases

Extract the reducer function `Array.prototype.reduce`

                  function getValue(object, propertyName) {
                    if (!propertyName) throw new Error("Impossible to set null property");

                    if (!object || typeof object === "undefined") return undefined;
                    return object[propertyName];
                  }

                  function getNestedValue(object, propertyName) {
                    const parts = propertyName.split(".");

                    const nestedValue = parts.reduce(getValue, object);

                    return nestedValue;
                  }

### Use Cases

Get rid of unnecessary code

                    function getValue(object, propertyName) {
                      if (!propertyName) throw new Error("Impossible to set null property");

                      return typeof object === "undefined" ? undefined : object[propertyName];
                    }

                    function getNestedValue(object, propertyName) {
                      return propertyName.split(".").reduce(getValue, object);
                    }

### Standard Library

#### `Array.prototype.{function}`

- `forEach`
- `filter`
- `map`
- `reduce`

### Standard Library

#### `Array.prototype.forEach`

                const elements = ["a", "b", "c"];

                for(let i = 0; i < elements.length; i++) {
                  console.log(elements[i]);
                }

                // functional equivalent
                elements.forEach(element => console.log(element));

Mostly used for side-effects

### Standard Library

#### `Array.prototype.filter`

                  const elements = [5, 10, 7];

                  const predicateEven = num => num % 2;
                  const predicateAboveEight = num => num > 8;

                  elements.filter(predicateEven); // [5, 7]
                  elements.filter(predicateAboveEight) // [10]

### Standard Library

#### `Array.prototype.map`

Convert a value into another one

                  // Simple Example
                  const elements = [5, 10, 7];
                  const timesTwo = num => num * 2;
                  elements.map(timesTwo); // [10, 20, 14]

                  // Advanced Example
                  const users = [
                    { name: "Donald", username: "realDonaldTrump" },
                    { name: "Hillary", username: "HillaryClinton" },
                    { name: "Barack", username: "BarackObama" }
                  ];

                  const usernames = users.map(user => user.username);
                  // ["realDonaldTrump", "HillaryClinton", "BarackObama"]

### Standard Library

#### `Array.prototype.reduce`

Combine multiple elements into an aggregate

                    const elements = [5, 10, 7];

                    const sum = (a, b) => a + b;
                    elements.reduce(sum, 0); // 22

                    const max = (a, b) => Math.max(a, b);
                    elements.reduce(max, -Infinity); // 10

### Standard Library

#### `Chaining`

                const users = [
                  { name: "Donald", username: "realDonaldTrump", president: true },
                  { name: "Hillary", username: "HillaryClinton", president: false },
                  { name: "Barack", username: "BarackObama", president: false }
                ];

                const isPresident = user => user.president;
                const introduceUser = user => `Hi! My name is ${user.name} and my Twitter is @${user.username}`;
                const log = msg => console.log(msg);

                users
                  .filter(isPresident)
                  .map(introduceUser)
                  .forEach(log);
                //  Hi! My name is Donald and my Twitter is @realDonaldTrump

### External Modules

#### Immutable.js

- build by Facebook
- Persistent Data Structures
- implements `Map`, `Set`, `List`
- keep old object, return new object

### External Modules

#### Immutable.js

                const numbers = [1, 2, 3];
                numbers.push(4); // numbers modified
                numbers; // [1, 2, 3, 4];

                const { List } = require('immutable');
                const immutableNumbers = new List([1, 2, 3]);
                immutableNumbers.push(4); // [1, 2, 3, 4]
                immutableNumbers; // [1, 2, 3]

                // Functions from earlier still work
                immutableNumbers.map(x => x * x); // [1, 4, 9]

### References & Further Links

- https://www.redotheweb.com/2015/09/18/declarative-imperative-js.html
- https://en.wikipedia.org/wiki/Functional_programming
- https://hackernoon.com/javascript-and-functional-programming-an-introduction-286aa625e26d
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array
- https://facebook.github.io/immutable-js/
- https://github.com/ramda/ramda
- http://elm-lang.org/