FUNCTIONAL PROGRAMMING STRIKES BACK

WE NEED TO TAKE A STEP BACK

Alonzo Church

LAMBDA FUNCTIONS

I BET YOU'RE ALREADY USING THEM

λx.x+x
lambda x: x + x

Lambda Calculus

Python

LAMBDA FUNCTIONS

I BET YOU'RE ALREADY USING THEM

λx.x+x
(x) => x + x;

Lambda Calculus

JavaScript

λx.λy.x  ;true
λx.λy.y  ;false
const TRUE  = x => y => x;
const FALSE = x => y => y;

Lambda Calculus

JavaScript

BOOLEANS?

WHO NEEDS BOOLEANS?

λz.(λx.(λy.zxy)) ;if/then/else
const IF = COND => THEN => ELSE => COND(THEN)(ELSE);

Lambda Calculus

CONTROL STRUCTURES?

WHO NEEDS THEM?

JavaScript

PURE FUNCTIONS, PURE CODEBASE

NON-PURE FUNCTIONAL LANGUAGES EXAMPLES

let approved = [];

for (let i = 0; i < users.length; i++) {
  if (users[i].score >= 7) {
    approved.push(approved);
  }
}
const approved = users.filter((user) => user.score >= 7);

Mutates a value

Does not mutate a value

PURITY MEANS

IMMUTABILITY

let user = { name: "John" };

function addLastName(lastName) {
  user.lastName = "Doe";
};
const addLastName = (obj, lastName) => ({ ...obj, lastName });

Causes a side effect

Does not cause a side effect

PURITY MEANS

NO SIDE EFFECTS

let result = 1;

for (let i = 2; i <= 5; i++) {
    result *= i;
}

console.log('Fact of 5: ', result);
const fact = n => n === 0 ? 1 : n * fact(n - 1);

console.log(`Fact of 5: ${fact(5)}`);

Uses loops

Uses recursion

PURITY MEANS

NO LOOPS

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class Main {

  public static void main(String[] args) {

    List<String> numbers = Arrays.asList("1", "2", "3", "4", "5", "6");
  
    List<Integer> even = numbers.stream()
                                .map(s -> Integer.valueOf(s))
                                .filter(number -> number % 2 == 0)
                                .collect(Collectors.toList());

    System.out.println("Even numbers in list: " + even);
  }

}

PURITY MEANS

NO LOOPS

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class Main {

  public static void main(String[] args) {
 
    List<String> numbers = Arrays.asList("1", "2", "3", "4", "5", "6");
  
    Integer sum = numbers.stream()
                         .map(s -> Integer.valueOf(s))
                         .reduce(0, (x, y) -> x + y);

    System.out.println("Sum of list is: " + sum);
  }

}

PURITY MEANS

NO LOOPS

let x = 10;

function addX(num) {
  return num + x;
}
function addX(num) {
  const x = 10;
  return num + x;
}

Result may change over time due to mutability

PURITY MEANS

REFERENTIAL TRANSPARENCY

Always produces the same result when the same arguments are given

let myCounter = 0;

while (true) {
  ...
  myCounter++;
};
function statelessCounter(iterations) {
  ...
  return statelessCounter(iteration + 1);
}

Updates the state on each iteration

PURITY MEANS

NO STATE

Creates a new "state" with every recursive function call

PURE FUNCTIONS, PURE CODEBASE

PURE FUNCTIONAL LANGUAGES EXAMPLES

greet :: [Char]
greet = "Hello World!"

"greet" is a function that returns a list of chars

PURITY MEANS

NO ASSIGNMENT STATEMENTS

main :: IO()
main = putStrLn "Hello World!"

Logging a string to the I/O means mutating the I/O stream

import System.Random
import Control.Monad (replicateM)

main = replicateM 10 (randomIO :: IO Float) >>= print

No randomness

PURITY MEANS

NO SIDE EFFECTS AT ALL

template <typename T>
void qsort (T *result, T *list, int n)
{
    if (n == 0) return;
    T *smallerList, *largerList;
    smallerList = new T[n];
    largerList = new T[n];      
    T pivot = list[0];
    int numSmaller=0, numLarger=0;      
    for (int i = 1; i < n; i++)
        if (list[i] < pivot)
            smallerList[numSmaller++] = list[i];
        else 
            largerList[numLarger++] = list[i];
    
    qsort(smallerList,smallerList,numSmaller); 
    qsort(largerList,largerList,numLarger);
    
    int pos = 0;        
    for ( int i = 0; i < numSmaller; i++)
        result[pos++] = smallerList[i];
    
    result[pos++] = pivot;
    
    for ( int i = 0; i < numLarger; i++)
        result[pos++] = largerList[i];
    
    delete [] smallerList;
    delete [] largerList;
};

QuickSort algorithm in C++

WHY PURITY MATTERS

PURE FUNCTIONS ARE EASIER TO REASON ABOUT

qsort :: Ord a => [a] -> [a]
qsort []     = []
qsort (x:xs) = qsort smaller ++ [x] ++ qsort bigger
  where
      smaller = filter (<  x) xs
      bigger  = filter (>= x) xs

QuickSort algorithm in Haskell

WHY PURITY MATTERS

PURE FUNCTIONS ARE EASIER TO REASON ABOUT

qsort :: Ord a => [a] -> [a]
qsort []     = []
qsort (x:xs) = qsort smaller ++ [x] ++ qsort bigger
  where
      smaller = filter (<  x) xs
      bigger  = filter (>= x) xs

WHY PURITY MATTERS

PURE FUNCTIONS ARE EASIER TO REASON ABOUT

function callPizzeria(phone: number): void { ... };
function waitForPizza(patience: any): void { ... };

Testing functions with side effects

WHY PURITY MATTERS

TESTING IS EASIER

test("Testing a method that has an hidden state and side-effects", () => {
  
  callPizzeria(phone);
  waitForPizza(patience);
  drinkBeer(can);
  
  let pizza = getPizzaFromPizzaboy(cash);
  
  expect(pizza).toBe(expectedPizza);
});
test("Testing a pure function is easier", () => {
  
  const result = compose( addOne, multiplyBy5, subtract5 )(10);
  
  expect(result).tobe(26);
  
});

Testing functions without side effects

WHY PURITY MATTERS

TESTING IS EASIER

def quicksort([]), do: []
def quicksort([pivot|t]) do
  quicksort(for x <- t, x < pivot, do: x)
  ++ [pivot] ++
  quicksort(for x <- t, x >= pivot, do: x)
end

Quicksort algorithm in Elixir

WHY PURITY MATTERS

DEBUGGING IS EASIER

qsort([]) -> [];
qsort([Pivot|Rest]) ->
  qsort([ X || X <- Rest, X < Pivot])
  ++ [Pivot] ++
  qsort([ Y || Y <- Rest, Y >= Pivot]).

Quicksort algorithm in Erlang

WHY PURITY MATTERS

DEBUGGING IS EASIER

let rec quicksort =
  function
  | [] -> []
  | x::xs ->
      let (smaller,larger) = List.partition (fun y  -> y < x) xs in
      (quicksort smaller) @ (x :: (quicksort larger))

Quicksort algorithm in OCaml

WHY PURITY MATTERS

DEBUGGING IS EASIER

let rec qsort = function
    hd :: tl ->
        let less, greater = List.partition ((>=) hd) tl
        List.concat [qsort less; [hd]; qsort greater]
    | _ -> []

Quicksort algorithm in F#

WHY PURITY MATTERS

DEBUGGING IS EASIER

(defn quicksort [seq]
  (if (emtpy? seq) []
    (let [x  (first seq)
          xs (rest seq)]
      (concat (quicksort (filter (fn [v] (<= v x)) xs))
              [x]
              (quicksort (filter (fn [v] (> v x)) xs))))))

Quicksort algorithm in Clojure

WHY PURITY MATTERS

DEBUGGING IS EASIER

def qsort: List[Int] => List[Int] = {
  case Nil => Nil
  case pivot :: tail =>
    val (smaller, rest) = tail.partition(_ < pivot)
    qsort(smaller) ::: pivot :: qsort(rest)
}

Quicksort algorithm in Scala

WHY PURITY MATTERS

DEBUGGING IS EASIER

WHY PURITY MATTERS

OUT-OF-THE BOX CONCURRENCY

WHY PURITY MATTERS

OUT-OF-THE BOX CONCURRENCY

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class Main {

  public static void main(String[] args) {
 
    List<String> numbers = Arrays.asList("1", "2", "3", "4", "5", "6");
  
    Integer sum = numbers.parallelStream()
                         .map(s -> Integer.valueOf(s))
                         .reduce(0, (x, y) -> x + y);

    System.out.println("Sum of list is: " + sum);
  }

}

PURITY MEANS

OUT-OF-THE BOX CONCURRENCY

PURE FUNCTIONS ARE

DETERMINISTIC

f (x) -> x + x

g (y) -> Y * y

h (z) -> z + 2

PURE FUNCTIONS ARE

COMPOSABLE

f (x) -> x + x

g (y) -> Y * y

h (z) -> z + 2

k (x) -> f(g(h(x)))

PURE FUNCTIONS ARE

DECLARATIVE

double   (x) -> x + x

pow      (y) -> Y * y

addTwo   (z) -> z + 2

makeMath (x) -> double(pow(addTwo(x)))

PURE FUNCTIONS ARE

DECLARATIVE

double   (x) -> x + x

pow      (y) -> Y * y

addTwo   (z) -> z + 2

makeMath (x) -> {
 
  result = 0

  result = double(x)
  result = pow(result)
  result = addTwo(result)

  return result
}

Declarative? Do you have any examples?

PURE FUNCTIONS ARE

DECLARATIVE

If we list all the natural numbers below 10 that are multiples of 3 or 5, we get 3, 5, 6 and 9. The sum of these multiples is 23.
Find the sum of all the multiples of 3 or 5 below 1000.

Project Euler, Problem #1 - https://projecteuler.net/problem=1

PURE FUNCTIONS ARE

DECLARATIVE

public class Main {
  
  public static void main(String[] args) {
  
    int i, sum = 0;
    
    for ( i = 3; i < 1000; i++) {

      if (i % 3 == 0 || i % 5 == 0 ) {
        sum += i;
      }
    }

    System.out.print(sum);
  }

}

PURE FUNCTIONS ARE

DECLARATIVE

import Data.List (union)

solution :: Int
solution = sum $ [3,6..limit] `union` [5,10..limit]
  where limit = 999

main :: IO()
main = print $ solution

PURE FUNCTIONS ARE

DECLARATIVE

def solution():
  return sum([i for i in range(1, 1000) 
    if i % 3 == 0 or i % 5 == 0])

PURE FUNCTIONS ARE

DECLARATIVE

const solution = [...Array(1000)]
	.map((_, i) => i)
	.filter(x => x % 3 === 0 || x % 5 === 0)
	.reduce((sum, x) => sum + x, 0);

PURE FUNCTIONS ARE

DECLARATIVE

const sum         = (x, y)   => x + y;
const isModZero   = (x, y)   => x % y === 0;
const or          = (x, y)   => (x || y);
const createArray = (length) => [...Array(length)].map((_, i) => i);

const solution = (limit) => (
  createArray(limit)
    .filter(x => or(isModZero(x, 5), isModZero(x, 3)))
    .reduce(sum, 0)
);

const result = solution(1000);
  • Extremely mature and battle-tested
  • Extremely Resilient
  • 99.999999999 (nine nines) uptime
  • Hot code reload

ERLANG

Used at

Used to build

  • Extremely mature and battle-tested
  • Extremely Resilient
  • 99.999999999 (nine nines) uptime
  • Hot code reload

ERLANG

  • Extremely mature and battle-tested
  • Incredible Type System
  • Hot code reload
  • Purely Functional

Used at

HASKELL

  • Incredibly Fast
  • Compiler targets everything
  • Extremely mature and battle-tested

OCAML

Used at

  • Runs on JVM
  • Built to scale
  • Great JVM interop

SCALA

Used at

Ruby

Java

JavaScript

.NET

I want to write rock solid frontend 

I want to start from scratch

Clojure

Scala

ReasonML

F#

Elm

PureScript

Haskell

Elixir

So should I get rid of my existing OOP/imperative code and refactor everything in a functional language?

So should I get rid of my existing OOP/imperative code and refactor everything in a functional language?

"Don't be a functional programmer.
Dont' be an object programmer.
Be a better programmer.
"

Brian Goetz, Java Language Architect at Oracle

LET'S KEEP IN TOUCH

MICHELE RIVA

Software Engineer at

Openmind

Founder of

JSMonday.dev

@MicheleRiva

@MicheleRivaCode

www.micheleriva.it

ciao@micheleriva.it

Made with Slides.com