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
Stack Wars
By Michele Riva
Stack Wars
Functional programming strikes back!
- 762