SlideShare una empresa de Scribd logo
1 de 122
Descargar para leer sin conexión
INTRODUCTION TO FP
(W/ JAVASCRIPT)
"A monad is just a monoid in the category of
endofunctors..."
WHY TO FP?
SOME FACTS:
(THAT MAY BLOW YOUR MIND)
Elegant, readable and simple code makes it hard for bugs to hide
70% of time spent while maintaining code is spent reading it
Global average for a coder's loc written p/ day is ~10
WHY TO FP?
Because FP "laws" and tools enables us to write a more:
Readable code
Declarative code
Reusable code
Testable code
In general, a more reliable & maintainable code in the long term
READABILITY CURVE
But the journey has its bumps
Source: https://github.com/getify/Functional-Light-JS/blob/master/ch1.md
A PRACTICAL INTRO TO FP
We'll go over some "mandatory" concepts:
First Class Functions
High-Order Functions & Closures
Function Purity
Managing Function Input
Function Composition
Value Immutability
Array Operations
Recursion
Monads Basics
A PRACTICAL INTRO TO FP
Some "mandatory" concepts:
First Class Functions
High-Order Functions & Closures
Function Purity
Managing Function Input
Function Composition
Value Immutability
Array Operations
Recursion
Monads Basics
FIRST CLASS FUNCTIONS
(OR FUNCTIONS AS VALUES)
The majority of patterns and tools around FP requires functions to
be treated as first-class citizens
Which means they can:
BE ASSIGNED TO VARIABLES
// anonymous functions
const aFunction = function () {
console.log('hello fp');
};
// or named functions
const aFunction = function aFunctionName() {
console.log('hello fp');
};
// or arrow functions
const aFunction = () => console.log('hello fp');
// or even borrowed methods
const aFunction = someObj.someOtherFunction;
BE ASSIGNED TO DATA STRUCTURES
// With objects
const obj = {
methodAnon: function() { },
methodNamed: function aFunctionName() { },
methodArrow: () => { },
methodBorrowed: otherObj.someOtherFunction;
};
// Or with arrays
const arr = [
function() { },
function aFunctionName() { },
() => { },
otherObj.someOtherFunction
];
BE USED AS OTHER FUNCTIONS ARGUMENTS
const hello = () => {
console.log('hello fp');
};
const callFn = fn => fn();
// ...
callFn(hello); // hello fp
BE RETURNED FROM OTHER FUNCTIONS
const getHello = () => {
return () => {
console.log('hello fp');
};
};
// or the shorter
const getHello = () => () => console.log('hello fp');
// ...
const hello = getHello();
hello(); // hello fp
// or in one go
getHello()(); // hello fp
A PRACTICAL INTRO TO FP
Some "mandatory" concepts:
First Class Functions
High-Order Functions & Closures
Function Purity
Managing Function Input
Function Composition
Value Immutability
Array Operations
Recursion
Monads Basics
HIGH-ORDER FUNCTIONS & CLOSURES
(OR WORKING WITH STATEFUL FUNCTIONS)
HIGH-ORDER FUNCTIONS
A high-order function is a function that does at least one of the
following:
1. TAKES ONE OR MORE FUNCTIONS AS
ARGUMENTS
Useful to separate concerns and abstract/decouple logic
const highOrderSecret = (fnArg) => {
const secret = 'FP rulez!';
fnArg(secret);
};
const logSecret = (secret) => console.log(secret);
const saveSecret = (secret) => secretStorage.add(secret);
// ...
highOrderSecret(logSecret); // FP rulez!
highOrderSecret(saveSecret);
2. RETURNS A FUNCTION AS ITS RESULT
Useful to "hide" state (achieve privacy), persist state to be
processed/used later and compose/add behaviour to other
functions
const makeSecret = () => {
const secret = 'FP rulez!';
return () => secret; // Btw, this is a closure
};
const getSecret = makeSecret();
console.log(getSecret()); // FP rulez!
CLOSURES
A closure is a function that refers to "free variables" (variables
defined in parent scopes)
In other words, it's a function that "remembers" the
state/environment where it was created
A CLOSER LOOK INTO A CLOSURE
// global scope
const makeSecret = () => {
// scope 0
const secret = 'FP rulez';
// following will log undefined because parent a scope
// does not have access to child scopes
console.log(secretSuffix); // ReferenceError: secretSuffix is not defined
return () => {
// scope 1
const secretSuffix = '!!!!!';
return secret + secretSuffix;
};
};
console.log(secret); // ReferenceError: secret is not defined
const getSecret = makeSecret();
// It remembers its own scope plus parent scopes
console.log(getSecret()); // FP rulez!!!!!
A PRACTICAL INTRO TO FP
Some "mandatory" concepts:
First Class Functions
High-Order Functions & Closures
Function Purity
Managing Function Input
Function Composition
Value Immutability
Array Operations
Recursion
Monads Basics
FUNCTION PURITY
(OR AVOIDING SIDE EFFECTS)
A function is considered pure if it does not break the following
"laws":
1. Always has to return the same output given the same input
2. Does not depend on/causes any side effect (state mutations, I/O
operations)
PURE FUNCTIONS
const add = (a, b) => a + b;
const getCircleArea = r => Math.PI * r * r;
const getFullName = (first, last) => `${first} ${last}`;
const logUserIn = user => Object.assign(
{},
user,
{ loggedIn: true }
);
IMPURE FUNCTIONS
// I/O operation
const logMsg = msg => console.log(msg);
// Different outputs, same input
const getRandom = (max) => Math.random() * max;
// depends on mutable state
const getFullName = (first, last) =>
`${globalNamePrefix} ${first} ${last}`;
// Mutating object state
const logUserIn = user => user.loggedIn = true;
A program without any observable side effect is also a program that
accomplishes nothing useful
but, side effects should be avoided where possible
as they make programs hard to follow/read, hard to test and hard to
maintain
most of a program codebase should be composed of small, single-
purpose and pure functions
A PRACTICAL INTRO TO FP
Some "mandatory" concepts:
First Class Functions
High-Order Functions & Closures
Function Purity
Managing Function Input
Function Composition
Value Immutability
Array Operations
Recursion
Monads Basics
MANAGING FUNCTION INPUT
(OR MANIPULATING FUNCTION ARGUMENTS)
ARGS VS PARAMS
Question: what's the difference between arguments and
parameters?
// firstName, middleName and lastName are parameters
const getFullName = (firstName, middleName, lastName) =>
`${firstName} ${middleName} ${lastName}`;
// All strings passed into getFullName() call are arguments
getFullName('Allan', 'Marques', 'Baptista');
// arguments < parameters - perfectly valid in JS
getFullName('Emperor', 'Palpatine');
// arguments > parameters - also valid
getFullName('Some', 'Big', 'Ass', 'Freaking', 'Name');
ARGS VS PARAMS
Parameter is the variable which is part of the function signature
Argument is the value/variable/reference/expression being passed
in during a function call
ARITY
The number of parameters a function expects in its signature is
called arity
It's possible to get a function's arity through the
Function.prototype.length property
const double = n => n * 2; // arity = 1 (unary)
// arity = 3 (ternary)
const getFullName = (firstName, middleName, lastName) =>
`${firstName} ${middleName} ${lastName}`;
const double = n => n * 2;
console.log(double.length); // 1
By combining the power of high-order functions (HoF), knowledge of
function arity and loose arguments application, we can build
powerful abstractions
FORCING UNARY FUNCTIONS
Sometimes we need to ensure a function that expects more than
one parameter to receive only one argument
That happens because parseInt's signature is:
parseInt(str [, radix])
And Array.prototype.map calls any function passed in with
the arguments:
fn(item, index, arr)
const strArr = ['1', '2', '3', '4', '5'];
const mumArr = strArr.map(parseInt);
console.log(numArr); // [1, NaN, NaN, NaN, NaN]
FORCING UNARY FUNCTIONS
We can fix that with a utility HoF usually called unary
That can be implemented in JS like so:
And used like this:
const unary = fn =>
param => fn(param);
const strArr = ['1', '2', '3', '4', '5'];
const mumArr = strArr.map(unary(parseInt));
console.log(numArr); // [1, 2, 3, 4, 5]
PARTIAL APPLICATION
Calling a function and passing some arguments to it like:
foo(bar, baz);
can also be described as applying function foo to the arguments
bar and baz
PARTIAL APPLICATION
Means fixing/binding a number of arguments to a function
producing another function with smaller arity
It's useful when we know some of the arguments that'll be applied
to a function ahead of time
But the rest of the arguments we'll only know at a later point in
execution time.
PARTIAL APPLICATION
A partial function application utility can easily be implemented like
so:
And it's used like this:
const partial = (fn, ...eagerArgs) =>
(...lazyArgs) => fn(...eagerArgs, ...lazyArgs);
const fullName = (preferedTreatment, firstName, lastName) =>
`${preferedTreatment} ${lastName}, ${firstName}`;
const maleName = partial(fullName, 'Sir');
const femaleName = partial(fullName, 'Ma'am');
maleName('Allan', 'Baptista'); // Sir Baptista, Allan
femaleName('Nadia', 'Carvalho'); // Ma'am Carvalho, Nadia
PARTIAL APPLICATION
It's also possible to implement a utility that partially applies the
final arguments like so:
That can be used like this:
const partialRight = (fn, ...rightArgs) =>
(...leftArgs) => fn(...leftArgs, ...rightArgs);
const fullName = (preferedTreatment, firstName, lastName) =>
`${preferedTreatment} ${lastName}, ${firstName}`;
const kirk = partialRight(fullName, 'James', 'Kirk');
kirk('Sir'); // Sir Kirk, James
kirk('Captain'); // Captain Kirk, James
CURRYING
It's creating a function that only executes its actual logic once it has
gathered all parameters it expects
When a curried function is applied to less arguments than its arity it
returns another function
And it keeps returning another function until all arguments are
provided
CURRYING
const curriedFullName = preferedTreatment =>
firstName =>
lastName =>
`${preferedTreatment} ${lastName}, ${firstName}`;
const getName = curriedFullName('Mr'); // preferedTreatment = 'Mr'
const getLastName = getName('James'); // firstName = 'James'
getLastName('Bond'); // Mr. Bond, James
// or in one go
curriedFullName('Sir')('Leonard')('Nimoy'); // Sir Nimoy, Leonard
CURRYING
In Haskell all functions are curried by default, but in javascript we
need to write a utility function to achieve the same
const autoCurry = (fn, arity = fn.length) =>
(...args) =>
args.length >= arity ?
fn(...args) :
autoCurry(partial(fn, ...args), arity - args.length);
CURRYING
const curriedFullName = autoCurry(
(preferedTreatment, firstName, lastName) =>
`${preferedTreatment} ${lastName}, ${firstName}`
);
const getName = curriedFullName('Mr'); // preferedTreatment = 'Mr'
const getLastName = getName('James'); // firstName = 'James'
getLastName('Bond'); // Mr. Bond, James
// or
curriedFullName('Sir')('Leonard')('Nimoy'); // Sir Nimoy, Leonard
// or
curriedFullName('Sir')('Rowan', 'Atkinson'); // Sir Atkinson, Rowan
// or
curriedFullName('Mr', 'Mickey', 'Mouse'); // Mr Mouse, Mickey
CURRYING
Note that the strict implementation of currying produces only
unary functions a er each call
So the implementation showed here should be called loose
currying, which is o en more useful
A PRACTICAL INTRO TO FP
Some "mandatory" concepts:
First Class Functions
High-Order Functions & Closures
Function Purity
Managing Function Input
Function Composition
Value Immutability
Array Operations
Recursion
Monads Basics
FUNCTION COMPOSITION
(OR PLAYING WITH BUILDING BLOCKS)
When a program/application is well split into simple, single-
purpose and pure functions a repeating pattern starts to come up:
And to avoid repetition, it's common to create composed
abstractions:
const outputData = freeze(enhance(escape(inputData)));
const transformData = data => freeze(enhance(escape(data)));
// later somewhere...
const outputData = transformData(inputData);
// and even later...
const dataToPersist = transformData(inputData);
A BETTER WAY
What if there was a way to achieve the same thing in a declarative
way?
const transformData = compose(freeze, enhance, escape);
// later somewhere...
const outputData = transformData(inputData);
compose(...fns) takes a list of functions
and returns another function that applies each function from right
to le , so:
// This
const transformData = compose(freeze, enhance, escape);
transformData(...args);
// is the same as this
const escaped = escape(...args);
const enhanced = enhance(escaped);
const outputData = freeze(enhanced);
// or this
const outputData = freeze(enhance(escape(...args)));
One can implement compose in JS like so:
const compose = (...fns) =>
(...args) => fns
.slice(0, -1)
.reduceRight(
(res, fn) => fn(res),
fns[fns.length - 1](...args)
);
FUNCTION COMPOSITION AND ARITY
Note that all functions besides the first one to be applied are
expected to be unary
as it's not possible to return more the one value from a function
By combining the concepts of function composition, arity and input
management one can build very complex logic in a very declarative
way
PIPING
Sometimes reading the flow of data from right to le can be
counter-intuitive
to fix that, we can build a variation of compose that applies each
function from le to right
that variation is usually called pipe or pipeline
const transformData = pipe(escape, enhance, freeze);
// later somewhere...
const outputData = transformData(inputData);
PIPING
pipe can be implemented like so:
Other than the difference on how data flows compose and pipe
works in the same way
(Except this implementation o pipe is a little bit more performant than compose's implementation showed
before)
const pipe = (firstFn, ...restFns) =>
(...args) => restFns.reduce(
(res, fn) => fn(res),
firstFn(...args)
);
A PRACTICAL INTRO TO FP
Some "mandatory" concepts:
First Class Functions
High-Order Functions & Closures
Function Purity
Managing Function Input
Function Composition
Value Immutability
Array Operations
Recursion
Monads Basics
VALUE IMMUTABILITY
(OR WRITING PREDICTABLE LOGIC)
In javascript (and the majority of hybrid/OO languages)
immutability is usually not natively enforced on objects
Some may naively think assigning objects with the const keyword
prevents objects from being mutated
But in fact, const only prevents the variable from being re-
assigned
const config = { cannotChange: 'Never changed' };
config.cannotChange = 'Chaos';
console.log(config); // { cannotChange: 'Chaos' }
// but the following throws a TypeError
config = { cannotChange: 'Invalid' };
THE CASE FOR IMMUTABILITY
Mutating an object is a side effect
Mutable objects are hard to follow/read
Mutable objects are hard to predict
Mutable objects o en are the source of hard-to-find bugs
Mutable objects are hard to debug
So, if immutability is not enforced natively by the language, how do
we achieve it?
IMMUTABILITY AS A CHOICE
PLAIN OBJECTS
// Very bad
const logIn = user => {
user.loggedIn = true;
return user;
};
const loggedUser = logIn(anonymousUser);
console.log(loggedUser.loggedIn); // true
console.log(anonymousUser.loggedIn); // true
IMMUTABILITY AS A CHOICE
PLAIN OBJECTS
Pattern: copy objects and mutate the copy
// Good
const logIn = user => {
const userCopy = Object.assign({}, user);
userCopy.loggedIn = true;
return userCopy;
};
const loggedUser = logIn(anonymousUser);
console.log(loggedUser.loggedIn); // true
console.log(anonymousUser.loggedIn); // false
IMMUTABILITY AS A CHOICE
ARRAYS
// Very bad
const addTask = (taskList, task) => {
taskList.push(add);
return taskList;
};
const newTaskList = addTask(taskList, task);
console.log(newTaskList.length); // 10
console.log(taskList.length); // 10
IMMUTABILITY AS A CHOICE
ARRAYS
Pattern: avoid mutable methods (push, pop, shi , unshi , splice,
sort, fill, reverse)
instead use immutable methods (concat, slice, map, filter) or the
spread notation
// Good
const addTask = (taskList, task) => {
// or [...taskList, task];
return taskList.concat(task);
};
const newTaskList = addTask(taskList, task);
console.log(newTaskList.length); // 10
console.log(taskList.length); // 9
IMMUTABILITY AS A LAW
Object.freeze freezes an object, preventing it from being
mutated (works w/ arrays as well)
Pattern: combine Object.freeze with other immutable
patterns to achieve full immutability
const user = Object.freeze({ name: 'Elza' });
user.name = 'Evil'; // throws if in 'strict mode'
console.log(user.name); // Elza
Note that Object.freeze only freezes objects shallowly
So to achieve full immutability all child objects also need to be
frozen
const user = Object.freeze({
name: {
first: 'Elza',
last: 'Arendelle'
}
});
user.name.first = 'Evil';
// { first: 'Evil', last: 'Arendelle' }
console.log(user.name);
Note that these patterns are much less performant than its mutable
counterpart
Even more if we're dealing with deep nested objects
If you need immutability as well as performance maybe it's time to
bring a library in
IMMUTABILITY LIBS
(by facebook)
(port of ClojureScript data structures)
ImmutableJS
mori
But if performance is still an issue, you should think about
replacing parts of your code with mutable patterns
but remember:
"Premature optimization is the root of all evil"
- Donald Knuth
A PRACTICAL INTRO TO FP
Some "mandatory" concepts:
First Class Functions
High-Order Functions & Closures
Function Purity
Managing Function Input
Function Composition
Value Immutability
Array Operations
Recursion
Monads Basics
ARRAY OPERATIONS
(OR MORE READABLE LOOPS)
ARRAY METHODS VS LOOPS
const activeItems = [];
for (let i = 0; i < arr.length; i++) {
if (arr[i].active === true) {
activeItems.push(arr[i]);
}
}
// vs
const activeItems = arr.filter(item => item.active === true);
ARRAY METHODS VS LOOPS
Array methods are usually better because:
Traversal logic is abstracted
Terser, more readable and declarative code
Functions and all its goodness!
 
Loops are better when:
Performance is needed (still, very questionable)
Need to break out of loop early
MAP()
Array.prototype.map is a HoF that traverses the list applying
the provided operator function to each item
and produces a new array with the values returned from each
operator call
const bananas = [' ', ' ', ' ', ' ', ' ', ' '];
const mix = bananas.map((banana, index) => (
index % 2 === 0 ? ' ' : banana
));
console.log(mix); // [' ', ' ', ' ', ' ', ' ', ' ']
MAP()
Source: https://github.com/getify/Functional-Light-JS/blob/master/ch8.md
A WORD ABOUT THE FEARED FUNCTOR
In FP terminology, a Functor is a wrapper object that has a utility
method for applying an operator function to its wrapped value
returning a new Functor wrapping the new value produced by the
operator
If the wrapped value is compound the Functor applies the operator
to each indidual value instead
All this is just a fancy way of saying that Functor is just an object
that has a map method
FILTER()
Array.prototype.filter is a HoF that traverses the list
applying the provided predicate function to each item
and produces a new array with the values of which the predicate
function returned truthy
const badDiet = [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '];
const goodDiet = badDiet.filter(food => !food.includes(' '));
console.log(goodDiet); // [' ', ' ', ' ', ' ']
FILTER()
Source: https://github.com/getify/Functional-Light-JS/blob/master/ch8.md
REDUCE()
Array.prototype.reduce is a HoF that traverses the list
applying the provided reducer function to the previous returned
value and current value
And produces whatever the last reducer call returns
const people = [' ', ' ', ' ', ' '];
const family = people.reduce((str, person) => (
str === '' ?
person :
str + 'u200D' + person
), '' /* <- initial value */);
console.log(family); // ' '
REDUCE()
Source: https://github.com/getify/Functional-Light-JS/blob/master/ch8.md
A PRACTICAL INTRO TO FP
Some "mandatory" concepts:
First Class Functions
High-Order Functions & Closures
Function Purity
Managing Function Input
Function Composition
Value Immutability
Array Operations
Recursion
Monads Basics
RECURSION
(OR RECURSION (OR RECURSION...))
Recursion is when a function calls itself until a base condition is
satisfied
const fib = n =>
n <= 1 ?
n :
fib(n - 2) + fib(n - 1);
fib(10); // 55
DECLARATIVE ITERATIONS
Although it may be less performant, expressing repetition with
recursion is usually more readable because of its declarative nature
const sum = (...values) => {
let total = 0;
for(let i = 0; i < values.length; i++) {
total += values[i];
}
return total;
};
// vs
const sum = (firstValue, ...otherValues) =>
otherValues.length === 0 ?
firstValue :
firstValue + sum(...otherValues);
"Loops may achieve a performance gain for your
program. Recursion may achieve a performance
gain for your programmer. Choose which is
more important in your situation!"
- Leigh Caldwell
DIVIDE AND CONQUER
An common strategy to apply when creating a recursive functions is
taking the divide and conquer approach:
Treat every list as a pair containing the first value and a list with
the rest of the values
Define the base condition
Define logic around the first value
Apply the function itself to the rest of the values
DIVIDE AND CONQUER
Iterative:
Recursive:
const map = (arr, fn) => {
const newArr = [];
for (let i = 0; i < arr.length; i++) {
newArr.push(fn(arr[i]));
}
return newArr;
};
const map = ([firstVal, ...rest], fn) =>
firstVal === undefined ?
[] :
[fn(firstVal), ...map(rest, fn)];
DIVIDE AND CONQUER
Note that this technique doesn't only work with lists. Lists are just
easier to wrap you head around the concept
This approach can be applied to almost anything
const iterativeRangeSum = (start, end) => {
let result = start;
for (let i = start; i < end; i++) {
result += i;
}
return result;
}
// vs
const recursiveRangeSum = (start, end) =>
start === end ?
0 :
start + recursiveRangeSum(start, end - 1);
STACK OVERFLOW
The function stack is a limited resource
If the base condition of a recursive function is not met until the
environment runs out of stack frames
The program/application will crash and burn
Always ensure the base condition will be satisfied before that or
refactor the function to use the benefits of tail call optimization
// If list has something like 200 items or more,
// Stack Overflow!
const values = recursiveMap(bigList, item => item.value);
TAIL CALL OPTIMIZATION
Tail call is when a function call is the very last thing evaluated inside
a function
When this happens the compiler can optimize the runtime by
reusing the last stack frame
const foo = () => {
const value = 'tail call';
return bar(value); // <- bar is being tail called
};
TAIL CALL OPTIMIZATION
By refactoring the recursive map utility from:
To use TCO:
map() will now support mapping over lists of any size
const map = ([firstVal, ...rest], fn) =>
firstVal === undefined ?
[] :
[fn(firstVal), ...map(rest, fn)];
const map = ([firstVal, ...rest], fn, result = []) =>
firstVal === undefined ?
result :
map(rest, fn, [...result, fn(firstVal)]);
A PRACTICAL INTRO TO FP
Some "mandatory" concepts:
First Class Functions
High-Order Functions & Closures
Function Purity
Managing Function Input
Function Composition
Value Immutability
Array Operations
Recursion
Monads Basics
MONADS BASICS
(OR THE THING THAT, ONCE UNDERSTOOD, YOU WON'T BE ABLE
TO EXPLAIN)
But first, some boring topics that won't be covered:
(but you should at some point)
Abstract Algebra
Category Theory
Type Theory
Some people say these fundamental topics are mandatory to start
FPing
I disaggree. But when you feel ready to dive into the theory behind
FP, it's' recommended you do so
REVISITING FUNCTORS
Meet something and nothing:
Note: the names of this Functor implementations vary a lot
(Some/None, Just/Nothing, Something/Empty...)
That happens because Functors, like any other FP type, is like a loose interface
Implementations must respect the type laws but their names are not enforced
const something = (value) => ({
map: fn => something(fn(value))
});
const nothing = () => ({
map: nothing
});
REVISITING FUNCTORS
something is useful
const getUser = userId => {
const user = repository.findUserById(userId);
return user ? something(user) : nothing();
}
// now we can write
// beautiful, idiomatic, declarative code
getUser(existingId) // User exists
.map(attachPermissions(permissions))
.map(attachOrders(orders))
.map(showProfile(template));
REVISITING FUNCTORS
nothing is useful
It's the same exact code, but nothing happens. Not even an
exception!
const getUser = userId => {
const user = repository.findUserById(userId);
return user ? something(user) : nothing();
}
// now we can write
// beautiful, idiomatic, declarative code
getUser(nonExistantId) // User is not found
.map(attachPermissions(permissions))
.map(attachOrders(orders))
.map(showProfile(profileTemplate));
REVISITING FUNCTORS
Maybe?
When we use the Something and Nothing Functors together in a
function like this, we're actually implementing the Maybe Functor.
It allows easier and safer null/empty checks without sacrificing
readability.
REVISITING FUNCTORS
handling branches/errors
const success = (value) => ({
map: fn => something(fn(value)),
catch: nothing
});
const error = (e) => ({
map: () => error(e),
catch: fn => something(fn(e))
});
REVISITING FUNCTORS
handling branches/errors
const getUser = userId => {
const user = repository.findUserById(userId);
return user ? success(user) : error(new Error('User not found'));
}
// now we can write
// beautiful, idiomatic, declarative code
getUser(nonExistantId) // User is not found
.map(attachPermissions(permissions))
.map(attachOrders(orders))
.map(showProfile(profileTemplate))
// error branch executed when is error
.catch(showError(errorTemplate));
REVISITING FUNCTORS
Either?
When we use the Error and Success together in a function like this,
we're actually implementing the Either Functor.
Its strict implementation is more complex, but the core concept is
the same.
In the strict implementation of the Either Functor, the Error and Success Functors are o en called Le and Right
respectively.
REVISITING FUNCTORS
Containing containers
const attachPermissions = permissions => user =>
permissions ?
something(user.setPermissions(permissions)) :
nothing();
const attachOrders = orders => user =>
orders ?
something(user.setOrders(orders)) :
nothing();
getUser(nonExistantId)
.map(attachPermissions(permissions)) // something(something(user))
.map(attachOrders(orders))// Error: setOrders is not a function
.map(showProfile(profileTemplate))
.catch(showError(errorTemplate));
MONADS TO THE RESCUE
Much like the Functor, the Monad has a utility method for applying
an operator function to its wrapped value
But unlike the Functors map() utility, it does not wrap the output
value in a new monad
Because it expects the value to be already wrapped in a monad
This utility function has many names: bind(), chain() and
flatMap()
MONADS TO THE RESCUE
Unboxing a box
const something = (value) => ({
map: fn => something(fn(value)),
flatMap: fn => fn(value)
});
const nothing = () => ({
map: nothing,
flatMap: nothing
});
MONADS TO THE RESCUE
Unboxing a box
const success = (value) => ({
map: fn => something(fn(value)),
flatMap: fn => fn(value)
catch: nothing
});
const error = (e) => ({
map: () => error(e),
flatMap: () => error(e),
catch: fn => something(fn(e))
});
MONADS TO THE RESCUE
Unboxing a box
const attachPermissions = permissions => user =>
permissions ?
something(user.setPermissions(permissions)) :
nothing();
const attachOrders = orders => user =>
orders ?
something(user.setOrders(orders)) :
nothing();
getUser(nonExistantId)
.flatMap(attachPermissions(permissions))// something(user)
.flatMap(attachOrders(orders))// something(user)
.map(showProfile(profileTemplate))
.catch(showError(errorTemplate));
YOU ARE USING MONADS ALREADY
Let's create a fictional monad that wraps a future value
That can be used like this:
const future = futureValue => ({
map: fn => future(fn(futureValue)),
flatMap: fn => fn(futureValue)
});
future(asyncGetUser(userId))
.flatMap(asyncAttatchPermissions(userId))
.flatMap(asyncAttatchOrders(userId))
.map(showProfile(profileTemplate));
YOU ARE USING MONADS ALREADY
But map() and flatMap() are not very meaningful when dealing
with future values
What if we "merged" and renamed them?
const future = futureValue => ({
then: fn => {
const nextFutureValue = fn(futureValue);
const isFutureMonad = (
nextFurureValue &&
typeof nextFutureValue.then === 'function'
);
return isFutureMonad ?
nextFutureValue :
future(nextFutureValue);
}
});
YOU ARE USING MONADS ALREADY
Now it reads a lot better:
future(asyncGetUser(userId))
.then(asyncAttatchPermissions(userId))
.then(asyncAttatchOrders(userId))
.then(showProfile(profileTemplate));
YOU ARE USING MONADS ALREADY
Feeling a déjà vu?
Yes! Promise is a Monad!
Promise.resolve(asyncGetUser(userId))
.then(asyncAttatchPermissions(userId))
.then(asyncAttatchOrders(userId))
.then(showProfile(profileTemplate));
FEEL BETRAYED?
IMPERATIVE VS DECLARATIVE
WHAT THIS CODE IS DOING?
const words = [
'The', 'quick', 'brown', 'fox,', 'jumps', 'over',
'the', 'lazy', 'dog.', '- It', 'was', 'a', 'german',
'shepherd!'
];
let result = false;
for (let i = 0; i < words.length; i++) {
words[i] = words[i].replace(/[ -_:;.,!?]/g, '');
if (words[i].length >= 3 && words[i].length <= 6) {
words[i] = words[i].toUpperCase()
.split('').reverse().join('');
if (words[i] === 'GOD') {
result = true;
break;
}
}
}
if (result) console.log('Found GOD...');
if (words[0] !== 'The') console.log('...and the devil');
AND THIS ONE?
const words = [
'The', 'quick', 'brown', 'fox,', 'jumps', 'over',
'the', 'lazy', 'dog.', '- It', 'was', 'a', 'german',
'shepherd!'
];
const removeInvalidChars = word => word.replace(/[ -_:;.,!?]/g, '');
const enoughChars = word => word.length >= 3;
const canContainGod = word => word.length >= 3 && word.length <= 6;
const toUpperCase = word => word.toUpperCase();
const reverseStr = word => word.split('').reverse().join('');
const toBibleCode = compose(reverseStr, toUpperCase);
const isGod = word => word === 'GOD';
const logIf = (condition, str) = condition && console.log(str);
const result = words
.map(removeInvalidChars)
.filter(canContainGod)
.map(toBibleCode)
.some(isGod);
logIf(result === true, 'Found GOD...');
logIf(words[0] !== 'The', '...and the devil');
ANSWER:
(ALMOST) THE SAME THING
Challenge: Can you spot a difference between both outputs?
DEVELOPER EVOLUTION
WHAT TO FP?
JS FP LIBS
- Functional utilities on top of lodash
- Functional utilities
- Functional utilities
- Suite of functional libraries
lodash/fp
Ramda
functional.js
Folktale
RESOURCES
- Great FP book, greatly inspired
this talk
- Awesome FP book, dives into
theory more
- A must read for every JS developer
- List of resources about FP in JS
Functional Light JS (by getify)
Mostly adequate guide to FP
JavaScript Allongé
Awesome FP JS
CONTACTS
Linkedin:
Github:
Skype:
E-mail:
/in/allanbaptista
m4n3z40
abaptista.daitan
abaptista@daitangroup.com
THANKS
I'm out

Más contenido relacionado

La actualidad más candente

Unit 4
Unit 4Unit 4
Unit 4siddr
 
Notes5
Notes5Notes5
Notes5hccit
 
Old Oracle Versions
Old Oracle VersionsOld Oracle Versions
Old Oracle VersionsJeffrey Kemp
 
Javascript fundamentals for php developers
Javascript fundamentals for php developersJavascript fundamentals for php developers
Javascript fundamentals for php developersChris Ramakers
 
How Functions Work
How Functions WorkHow Functions Work
How Functions WorkSaumil Shah
 
React Js Training In Bangalore | ES6 Concepts in Depth
React Js Training   In Bangalore | ES6  Concepts in DepthReact Js Training   In Bangalore | ES6  Concepts in Depth
React Js Training In Bangalore | ES6 Concepts in DepthSiva Vadlamudi
 
Exploit Research and Development Megaprimer: Unicode Based Exploit Development
Exploit Research and Development Megaprimer: Unicode Based Exploit DevelopmentExploit Research and Development Megaprimer: Unicode Based Exploit Development
Exploit Research and Development Megaprimer: Unicode Based Exploit DevelopmentAjin Abraham
 
Attributes Unwrapped: Lessons under the surface of active record
Attributes Unwrapped: Lessons under the surface of active recordAttributes Unwrapped: Lessons under the surface of active record
Attributes Unwrapped: Lessons under the surface of active record.toster
 
2021.laravelconf.tw.slides2
2021.laravelconf.tw.slides22021.laravelconf.tw.slides2
2021.laravelconf.tw.slides2LiviaLiaoFontech
 
The Ring programming language version 1.10 book - Part 97 of 212
The Ring programming language version 1.10 book - Part 97 of 212The Ring programming language version 1.10 book - Part 97 of 212
The Ring programming language version 1.10 book - Part 97 of 212Mahmoud Samir Fayed
 
JavaScript Functions
JavaScript FunctionsJavaScript Functions
JavaScript FunctionsColin DeCarlo
 
Ejemplo completo de integración JLex y CUP
Ejemplo completo de integración JLex y CUPEjemplo completo de integración JLex y CUP
Ejemplo completo de integración JLex y CUPEgdares Futch H.
 
An introduction to ROP
An introduction to ROPAn introduction to ROP
An introduction to ROPSaumil Shah
 

La actualidad más candente (20)

PHP7 is coming
PHP7 is comingPHP7 is coming
PHP7 is coming
 
Unit 4
Unit 4Unit 4
Unit 4
 
Php string function
Php string function Php string function
Php string function
 
Notes5
Notes5Notes5
Notes5
 
Old Oracle Versions
Old Oracle VersionsOld Oracle Versions
Old Oracle Versions
 
Javascript fundamentals for php developers
Javascript fundamentals for php developersJavascript fundamentals for php developers
Javascript fundamentals for php developers
 
How Functions Work
How Functions WorkHow Functions Work
How Functions Work
 
perltut
perltutperltut
perltut
 
C++ aptitude
C++ aptitudeC++ aptitude
C++ aptitude
 
React Js Training In Bangalore | ES6 Concepts in Depth
React Js Training   In Bangalore | ES6  Concepts in DepthReact Js Training   In Bangalore | ES6  Concepts in Depth
React Js Training In Bangalore | ES6 Concepts in Depth
 
Exploit Research and Development Megaprimer: Unicode Based Exploit Development
Exploit Research and Development Megaprimer: Unicode Based Exploit DevelopmentExploit Research and Development Megaprimer: Unicode Based Exploit Development
Exploit Research and Development Megaprimer: Unicode Based Exploit Development
 
Attributes Unwrapped: Lessons under the surface of active record
Attributes Unwrapped: Lessons under the surface of active recordAttributes Unwrapped: Lessons under the surface of active record
Attributes Unwrapped: Lessons under the surface of active record
 
Advance ROP Attacks
Advance ROP AttacksAdvance ROP Attacks
Advance ROP Attacks
 
Introduction to Perl Programming
Introduction to Perl ProgrammingIntroduction to Perl Programming
Introduction to Perl Programming
 
2021.laravelconf.tw.slides2
2021.laravelconf.tw.slides22021.laravelconf.tw.slides2
2021.laravelconf.tw.slides2
 
The Ring programming language version 1.10 book - Part 97 of 212
The Ring programming language version 1.10 book - Part 97 of 212The Ring programming language version 1.10 book - Part 97 of 212
The Ring programming language version 1.10 book - Part 97 of 212
 
JavaScript Functions
JavaScript FunctionsJavaScript Functions
JavaScript Functions
 
Ejemplo completo de integración JLex y CUP
Ejemplo completo de integración JLex y CUPEjemplo completo de integración JLex y CUP
Ejemplo completo de integración JLex y CUP
 
An introduction to ROP
An introduction to ROPAn introduction to ROP
An introduction to ROP
 
Functions
FunctionsFunctions
Functions
 

Similar a Introduction to Functional Programming (w/ JS)

Php Reusing Code And Writing Functions
Php Reusing Code And Writing FunctionsPhp Reusing Code And Writing Functions
Php Reusing Code And Writing Functionsmussawir20
 
Chapter 11 Function
Chapter 11 FunctionChapter 11 Function
Chapter 11 FunctionDeepak Singh
 
php user defined functions
php user defined functionsphp user defined functions
php user defined functionsvishnupriyapm4
 
Introduction to Functional Programming
Introduction to Functional ProgrammingIntroduction to Functional Programming
Introduction to Functional ProgrammingHoàng Lâm Huỳnh
 
JavaScript: The Language
JavaScript: The LanguageJavaScript: The Language
JavaScript: The LanguageEngage Software
 
Swift Programming Language
Swift Programming LanguageSwift Programming Language
Swift Programming LanguageGiuseppe Arici
 
Asynchronous programming done right - Node.js
Asynchronous programming done right - Node.jsAsynchronous programming done right - Node.js
Asynchronous programming done right - Node.jsPiotr Pelczar
 
Basic information of function in cpu
Basic information of function in cpuBasic information of function in cpu
Basic information of function in cpuDhaval Jalalpara
 
04. WORKING WITH FUNCTIONS-2 (1).pptx
04. WORKING WITH FUNCTIONS-2 (1).pptx04. WORKING WITH FUNCTIONS-2 (1).pptx
04. WORKING WITH FUNCTIONS-2 (1).pptxManas40552
 
PHP-03-Functions.ppt
PHP-03-Functions.pptPHP-03-Functions.ppt
PHP-03-Functions.pptJamers2
 
1669958779195.pdf
1669958779195.pdf1669958779195.pdf
1669958779195.pdfvenud11
 

Similar a Introduction to Functional Programming (w/ JS) (20)

Closures
ClosuresClosures
Closures
 
Licão 13 functions
Licão 13 functionsLicão 13 functions
Licão 13 functions
 
Php Reusing Code And Writing Functions
Php Reusing Code And Writing FunctionsPhp Reusing Code And Writing Functions
Php Reusing Code And Writing Functions
 
Function C++
Function C++ Function C++
Function C++
 
Function in C++
Function in C++Function in C++
Function in C++
 
Chapter 11 Function
Chapter 11 FunctionChapter 11 Function
Chapter 11 Function
 
php user defined functions
php user defined functionsphp user defined functions
php user defined functions
 
PHP slides
PHP slidesPHP slides
PHP slides
 
Introduction to Functional Programming
Introduction to Functional ProgrammingIntroduction to Functional Programming
Introduction to Functional Programming
 
Fp201 unit5 1
Fp201 unit5 1Fp201 unit5 1
Fp201 unit5 1
 
JavaScript: The Language
JavaScript: The LanguageJavaScript: The Language
JavaScript: The Language
 
Swift Programming Language
Swift Programming LanguageSwift Programming Language
Swift Programming Language
 
ES6 metaprogramming unleashed
ES6 metaprogramming unleashedES6 metaprogramming unleashed
ES6 metaprogramming unleashed
 
Asynchronous programming done right - Node.js
Asynchronous programming done right - Node.jsAsynchronous programming done right - Node.js
Asynchronous programming done right - Node.js
 
functions new.pptx
functions new.pptxfunctions new.pptx
functions new.pptx
 
Basic information of function in cpu
Basic information of function in cpuBasic information of function in cpu
Basic information of function in cpu
 
04. WORKING WITH FUNCTIONS-2 (1).pptx
04. WORKING WITH FUNCTIONS-2 (1).pptx04. WORKING WITH FUNCTIONS-2 (1).pptx
04. WORKING WITH FUNCTIONS-2 (1).pptx
 
PHP-03-Functions.ppt
PHP-03-Functions.pptPHP-03-Functions.ppt
PHP-03-Functions.ppt
 
PHP-03-Functions.ppt
PHP-03-Functions.pptPHP-03-Functions.ppt
PHP-03-Functions.ppt
 
1669958779195.pdf
1669958779195.pdf1669958779195.pdf
1669958779195.pdf
 

Último

GenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationGenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationMichael W. Hawkins
 
presentation ICT roal in 21st century education
presentation ICT roal in 21st century educationpresentation ICT roal in 21st century education
presentation ICT roal in 21st century educationjfdjdjcjdnsjd
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerThousandEyes
 
Understanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdfUnderstanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdfUK Journal
 
IAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI SolutionsIAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI SolutionsEnterprise Knowledge
 
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024The Digital Insurer
 
Evaluating the top large language models.pdf
Evaluating the top large language models.pdfEvaluating the top large language models.pdf
Evaluating the top large language models.pdfChristopherTHyatt
 
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Miguel Araújo
 
A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)Gabriella Davis
 
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot TakeoffStrategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoffsammart93
 
Handwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed textsHandwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed textsMaria Levchenko
 
08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking MenDelhi Call girls
 
Boost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityBoost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityPrincipled Technologies
 
GenAI Risks & Security Meetup 01052024.pdf
GenAI Risks & Security Meetup 01052024.pdfGenAI Risks & Security Meetup 01052024.pdf
GenAI Risks & Security Meetup 01052024.pdflior mazor
 
Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...Enterprise Knowledge
 
CNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of ServiceCNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of Servicegiselly40
 
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemkeProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemkeProduct Anonymous
 
Tech Trends Report 2024 Future Today Institute.pdf
Tech Trends Report 2024 Future Today Institute.pdfTech Trends Report 2024 Future Today Institute.pdf
Tech Trends Report 2024 Future Today Institute.pdfhans926745
 
Histor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slideHistor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slidevu2urc
 
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law DevelopmentsTrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law DevelopmentsTrustArc
 

Último (20)

GenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationGenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day Presentation
 
presentation ICT roal in 21st century education
presentation ICT roal in 21st century educationpresentation ICT roal in 21st century education
presentation ICT roal in 21st century education
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 
Understanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdfUnderstanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdf
 
IAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI SolutionsIAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI Solutions
 
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
 
Evaluating the top large language models.pdf
Evaluating the top large language models.pdfEvaluating the top large language models.pdf
Evaluating the top large language models.pdf
 
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
 
A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)
 
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot TakeoffStrategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
 
Handwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed textsHandwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed texts
 
08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men
 
Boost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityBoost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivity
 
GenAI Risks & Security Meetup 01052024.pdf
GenAI Risks & Security Meetup 01052024.pdfGenAI Risks & Security Meetup 01052024.pdf
GenAI Risks & Security Meetup 01052024.pdf
 
Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...
 
CNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of ServiceCNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of Service
 
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemkeProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
 
Tech Trends Report 2024 Future Today Institute.pdf
Tech Trends Report 2024 Future Today Institute.pdfTech Trends Report 2024 Future Today Institute.pdf
Tech Trends Report 2024 Future Today Institute.pdf
 
Histor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slideHistor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slide
 
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law DevelopmentsTrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
 

Introduction to Functional Programming (w/ JS)

  • 2. "A monad is just a monoid in the category of endofunctors..."
  • 3.
  • 5. SOME FACTS: (THAT MAY BLOW YOUR MIND) Elegant, readable and simple code makes it hard for bugs to hide 70% of time spent while maintaining code is spent reading it Global average for a coder's loc written p/ day is ~10
  • 6.
  • 7. WHY TO FP? Because FP "laws" and tools enables us to write a more: Readable code Declarative code Reusable code Testable code In general, a more reliable & maintainable code in the long term
  • 8. READABILITY CURVE But the journey has its bumps Source: https://github.com/getify/Functional-Light-JS/blob/master/ch1.md
  • 9. A PRACTICAL INTRO TO FP We'll go over some "mandatory" concepts: First Class Functions High-Order Functions & Closures Function Purity Managing Function Input Function Composition Value Immutability Array Operations Recursion Monads Basics
  • 10. A PRACTICAL INTRO TO FP Some "mandatory" concepts: First Class Functions High-Order Functions & Closures Function Purity Managing Function Input Function Composition Value Immutability Array Operations Recursion Monads Basics
  • 11. FIRST CLASS FUNCTIONS (OR FUNCTIONS AS VALUES)
  • 12. The majority of patterns and tools around FP requires functions to be treated as first-class citizens Which means they can:
  • 13. BE ASSIGNED TO VARIABLES // anonymous functions const aFunction = function () { console.log('hello fp'); }; // or named functions const aFunction = function aFunctionName() { console.log('hello fp'); }; // or arrow functions const aFunction = () => console.log('hello fp'); // or even borrowed methods const aFunction = someObj.someOtherFunction;
  • 14. BE ASSIGNED TO DATA STRUCTURES // With objects const obj = { methodAnon: function() { }, methodNamed: function aFunctionName() { }, methodArrow: () => { }, methodBorrowed: otherObj.someOtherFunction; }; // Or with arrays const arr = [ function() { }, function aFunctionName() { }, () => { }, otherObj.someOtherFunction ];
  • 15. BE USED AS OTHER FUNCTIONS ARGUMENTS const hello = () => { console.log('hello fp'); }; const callFn = fn => fn(); // ... callFn(hello); // hello fp
  • 16. BE RETURNED FROM OTHER FUNCTIONS const getHello = () => { return () => { console.log('hello fp'); }; }; // or the shorter const getHello = () => () => console.log('hello fp'); // ... const hello = getHello(); hello(); // hello fp // or in one go getHello()(); // hello fp
  • 17. A PRACTICAL INTRO TO FP Some "mandatory" concepts: First Class Functions High-Order Functions & Closures Function Purity Managing Function Input Function Composition Value Immutability Array Operations Recursion Monads Basics
  • 18. HIGH-ORDER FUNCTIONS & CLOSURES (OR WORKING WITH STATEFUL FUNCTIONS)
  • 19. HIGH-ORDER FUNCTIONS A high-order function is a function that does at least one of the following:
  • 20. 1. TAKES ONE OR MORE FUNCTIONS AS ARGUMENTS Useful to separate concerns and abstract/decouple logic const highOrderSecret = (fnArg) => { const secret = 'FP rulez!'; fnArg(secret); }; const logSecret = (secret) => console.log(secret); const saveSecret = (secret) => secretStorage.add(secret); // ... highOrderSecret(logSecret); // FP rulez! highOrderSecret(saveSecret);
  • 21. 2. RETURNS A FUNCTION AS ITS RESULT Useful to "hide" state (achieve privacy), persist state to be processed/used later and compose/add behaviour to other functions const makeSecret = () => { const secret = 'FP rulez!'; return () => secret; // Btw, this is a closure }; const getSecret = makeSecret(); console.log(getSecret()); // FP rulez!
  • 22. CLOSURES A closure is a function that refers to "free variables" (variables defined in parent scopes) In other words, it's a function that "remembers" the state/environment where it was created
  • 23. A CLOSER LOOK INTO A CLOSURE // global scope const makeSecret = () => { // scope 0 const secret = 'FP rulez'; // following will log undefined because parent a scope // does not have access to child scopes console.log(secretSuffix); // ReferenceError: secretSuffix is not defined return () => { // scope 1 const secretSuffix = '!!!!!'; return secret + secretSuffix; }; }; console.log(secret); // ReferenceError: secret is not defined const getSecret = makeSecret(); // It remembers its own scope plus parent scopes console.log(getSecret()); // FP rulez!!!!!
  • 24. A PRACTICAL INTRO TO FP Some "mandatory" concepts: First Class Functions High-Order Functions & Closures Function Purity Managing Function Input Function Composition Value Immutability Array Operations Recursion Monads Basics
  • 26. A function is considered pure if it does not break the following "laws": 1. Always has to return the same output given the same input 2. Does not depend on/causes any side effect (state mutations, I/O operations)
  • 27. PURE FUNCTIONS const add = (a, b) => a + b; const getCircleArea = r => Math.PI * r * r; const getFullName = (first, last) => `${first} ${last}`; const logUserIn = user => Object.assign( {}, user, { loggedIn: true } );
  • 28. IMPURE FUNCTIONS // I/O operation const logMsg = msg => console.log(msg); // Different outputs, same input const getRandom = (max) => Math.random() * max; // depends on mutable state const getFullName = (first, last) => `${globalNamePrefix} ${first} ${last}`; // Mutating object state const logUserIn = user => user.loggedIn = true;
  • 29. A program without any observable side effect is also a program that accomplishes nothing useful but, side effects should be avoided where possible as they make programs hard to follow/read, hard to test and hard to maintain most of a program codebase should be composed of small, single- purpose and pure functions
  • 30. A PRACTICAL INTRO TO FP Some "mandatory" concepts: First Class Functions High-Order Functions & Closures Function Purity Managing Function Input Function Composition Value Immutability Array Operations Recursion Monads Basics
  • 31. MANAGING FUNCTION INPUT (OR MANIPULATING FUNCTION ARGUMENTS)
  • 32. ARGS VS PARAMS Question: what's the difference between arguments and parameters? // firstName, middleName and lastName are parameters const getFullName = (firstName, middleName, lastName) => `${firstName} ${middleName} ${lastName}`; // All strings passed into getFullName() call are arguments getFullName('Allan', 'Marques', 'Baptista'); // arguments < parameters - perfectly valid in JS getFullName('Emperor', 'Palpatine'); // arguments > parameters - also valid getFullName('Some', 'Big', 'Ass', 'Freaking', 'Name');
  • 33. ARGS VS PARAMS Parameter is the variable which is part of the function signature Argument is the value/variable/reference/expression being passed in during a function call
  • 34. ARITY The number of parameters a function expects in its signature is called arity It's possible to get a function's arity through the Function.prototype.length property const double = n => n * 2; // arity = 1 (unary) // arity = 3 (ternary) const getFullName = (firstName, middleName, lastName) => `${firstName} ${middleName} ${lastName}`; const double = n => n * 2; console.log(double.length); // 1
  • 35. By combining the power of high-order functions (HoF), knowledge of function arity and loose arguments application, we can build powerful abstractions
  • 36. FORCING UNARY FUNCTIONS Sometimes we need to ensure a function that expects more than one parameter to receive only one argument That happens because parseInt's signature is: parseInt(str [, radix]) And Array.prototype.map calls any function passed in with the arguments: fn(item, index, arr) const strArr = ['1', '2', '3', '4', '5']; const mumArr = strArr.map(parseInt); console.log(numArr); // [1, NaN, NaN, NaN, NaN]
  • 37. FORCING UNARY FUNCTIONS We can fix that with a utility HoF usually called unary That can be implemented in JS like so: And used like this: const unary = fn => param => fn(param); const strArr = ['1', '2', '3', '4', '5']; const mumArr = strArr.map(unary(parseInt)); console.log(numArr); // [1, 2, 3, 4, 5]
  • 38. PARTIAL APPLICATION Calling a function and passing some arguments to it like: foo(bar, baz); can also be described as applying function foo to the arguments bar and baz
  • 39. PARTIAL APPLICATION Means fixing/binding a number of arguments to a function producing another function with smaller arity It's useful when we know some of the arguments that'll be applied to a function ahead of time But the rest of the arguments we'll only know at a later point in execution time.
  • 40. PARTIAL APPLICATION A partial function application utility can easily be implemented like so: And it's used like this: const partial = (fn, ...eagerArgs) => (...lazyArgs) => fn(...eagerArgs, ...lazyArgs); const fullName = (preferedTreatment, firstName, lastName) => `${preferedTreatment} ${lastName}, ${firstName}`; const maleName = partial(fullName, 'Sir'); const femaleName = partial(fullName, 'Ma'am'); maleName('Allan', 'Baptista'); // Sir Baptista, Allan femaleName('Nadia', 'Carvalho'); // Ma'am Carvalho, Nadia
  • 41. PARTIAL APPLICATION It's also possible to implement a utility that partially applies the final arguments like so: That can be used like this: const partialRight = (fn, ...rightArgs) => (...leftArgs) => fn(...leftArgs, ...rightArgs); const fullName = (preferedTreatment, firstName, lastName) => `${preferedTreatment} ${lastName}, ${firstName}`; const kirk = partialRight(fullName, 'James', 'Kirk'); kirk('Sir'); // Sir Kirk, James kirk('Captain'); // Captain Kirk, James
  • 42. CURRYING It's creating a function that only executes its actual logic once it has gathered all parameters it expects When a curried function is applied to less arguments than its arity it returns another function And it keeps returning another function until all arguments are provided
  • 43. CURRYING const curriedFullName = preferedTreatment => firstName => lastName => `${preferedTreatment} ${lastName}, ${firstName}`; const getName = curriedFullName('Mr'); // preferedTreatment = 'Mr' const getLastName = getName('James'); // firstName = 'James' getLastName('Bond'); // Mr. Bond, James // or in one go curriedFullName('Sir')('Leonard')('Nimoy'); // Sir Nimoy, Leonard
  • 44. CURRYING In Haskell all functions are curried by default, but in javascript we need to write a utility function to achieve the same const autoCurry = (fn, arity = fn.length) => (...args) => args.length >= arity ? fn(...args) : autoCurry(partial(fn, ...args), arity - args.length);
  • 45. CURRYING const curriedFullName = autoCurry( (preferedTreatment, firstName, lastName) => `${preferedTreatment} ${lastName}, ${firstName}` ); const getName = curriedFullName('Mr'); // preferedTreatment = 'Mr' const getLastName = getName('James'); // firstName = 'James' getLastName('Bond'); // Mr. Bond, James // or curriedFullName('Sir')('Leonard')('Nimoy'); // Sir Nimoy, Leonard // or curriedFullName('Sir')('Rowan', 'Atkinson'); // Sir Atkinson, Rowan // or curriedFullName('Mr', 'Mickey', 'Mouse'); // Mr Mouse, Mickey
  • 46. CURRYING Note that the strict implementation of currying produces only unary functions a er each call So the implementation showed here should be called loose currying, which is o en more useful
  • 47. A PRACTICAL INTRO TO FP Some "mandatory" concepts: First Class Functions High-Order Functions & Closures Function Purity Managing Function Input Function Composition Value Immutability Array Operations Recursion Monads Basics
  • 48. FUNCTION COMPOSITION (OR PLAYING WITH BUILDING BLOCKS)
  • 49. When a program/application is well split into simple, single- purpose and pure functions a repeating pattern starts to come up: And to avoid repetition, it's common to create composed abstractions: const outputData = freeze(enhance(escape(inputData))); const transformData = data => freeze(enhance(escape(data))); // later somewhere... const outputData = transformData(inputData); // and even later... const dataToPersist = transformData(inputData);
  • 50. A BETTER WAY What if there was a way to achieve the same thing in a declarative way? const transformData = compose(freeze, enhance, escape); // later somewhere... const outputData = transformData(inputData);
  • 51. compose(...fns) takes a list of functions and returns another function that applies each function from right to le , so: // This const transformData = compose(freeze, enhance, escape); transformData(...args); // is the same as this const escaped = escape(...args); const enhanced = enhance(escaped); const outputData = freeze(enhanced); // or this const outputData = freeze(enhance(escape(...args)));
  • 52. One can implement compose in JS like so: const compose = (...fns) => (...args) => fns .slice(0, -1) .reduceRight( (res, fn) => fn(res), fns[fns.length - 1](...args) );
  • 53. FUNCTION COMPOSITION AND ARITY Note that all functions besides the first one to be applied are expected to be unary as it's not possible to return more the one value from a function By combining the concepts of function composition, arity and input management one can build very complex logic in a very declarative way
  • 54. PIPING Sometimes reading the flow of data from right to le can be counter-intuitive to fix that, we can build a variation of compose that applies each function from le to right that variation is usually called pipe or pipeline const transformData = pipe(escape, enhance, freeze); // later somewhere... const outputData = transformData(inputData);
  • 55. PIPING pipe can be implemented like so: Other than the difference on how data flows compose and pipe works in the same way (Except this implementation o pipe is a little bit more performant than compose's implementation showed before) const pipe = (firstFn, ...restFns) => (...args) => restFns.reduce( (res, fn) => fn(res), firstFn(...args) );
  • 56. A PRACTICAL INTRO TO FP Some "mandatory" concepts: First Class Functions High-Order Functions & Closures Function Purity Managing Function Input Function Composition Value Immutability Array Operations Recursion Monads Basics
  • 57. VALUE IMMUTABILITY (OR WRITING PREDICTABLE LOGIC)
  • 58. In javascript (and the majority of hybrid/OO languages) immutability is usually not natively enforced on objects Some may naively think assigning objects with the const keyword prevents objects from being mutated
  • 59. But in fact, const only prevents the variable from being re- assigned const config = { cannotChange: 'Never changed' }; config.cannotChange = 'Chaos'; console.log(config); // { cannotChange: 'Chaos' } // but the following throws a TypeError config = { cannotChange: 'Invalid' };
  • 60. THE CASE FOR IMMUTABILITY Mutating an object is a side effect Mutable objects are hard to follow/read Mutable objects are hard to predict Mutable objects o en are the source of hard-to-find bugs Mutable objects are hard to debug So, if immutability is not enforced natively by the language, how do we achieve it?
  • 61. IMMUTABILITY AS A CHOICE PLAIN OBJECTS // Very bad const logIn = user => { user.loggedIn = true; return user; }; const loggedUser = logIn(anonymousUser); console.log(loggedUser.loggedIn); // true console.log(anonymousUser.loggedIn); // true
  • 62. IMMUTABILITY AS A CHOICE PLAIN OBJECTS Pattern: copy objects and mutate the copy // Good const logIn = user => { const userCopy = Object.assign({}, user); userCopy.loggedIn = true; return userCopy; }; const loggedUser = logIn(anonymousUser); console.log(loggedUser.loggedIn); // true console.log(anonymousUser.loggedIn); // false
  • 63. IMMUTABILITY AS A CHOICE ARRAYS // Very bad const addTask = (taskList, task) => { taskList.push(add); return taskList; }; const newTaskList = addTask(taskList, task); console.log(newTaskList.length); // 10 console.log(taskList.length); // 10
  • 64. IMMUTABILITY AS A CHOICE ARRAYS Pattern: avoid mutable methods (push, pop, shi , unshi , splice, sort, fill, reverse) instead use immutable methods (concat, slice, map, filter) or the spread notation // Good const addTask = (taskList, task) => { // or [...taskList, task]; return taskList.concat(task); }; const newTaskList = addTask(taskList, task); console.log(newTaskList.length); // 10 console.log(taskList.length); // 9
  • 65. IMMUTABILITY AS A LAW Object.freeze freezes an object, preventing it from being mutated (works w/ arrays as well) Pattern: combine Object.freeze with other immutable patterns to achieve full immutability const user = Object.freeze({ name: 'Elza' }); user.name = 'Evil'; // throws if in 'strict mode' console.log(user.name); // Elza
  • 66. Note that Object.freeze only freezes objects shallowly So to achieve full immutability all child objects also need to be frozen const user = Object.freeze({ name: { first: 'Elza', last: 'Arendelle' } }); user.name.first = 'Evil'; // { first: 'Evil', last: 'Arendelle' } console.log(user.name);
  • 67. Note that these patterns are much less performant than its mutable counterpart Even more if we're dealing with deep nested objects If you need immutability as well as performance maybe it's time to bring a library in
  • 68. IMMUTABILITY LIBS (by facebook) (port of ClojureScript data structures) ImmutableJS mori
  • 69. But if performance is still an issue, you should think about replacing parts of your code with mutable patterns but remember: "Premature optimization is the root of all evil" - Donald Knuth
  • 70. A PRACTICAL INTRO TO FP Some "mandatory" concepts: First Class Functions High-Order Functions & Closures Function Purity Managing Function Input Function Composition Value Immutability Array Operations Recursion Monads Basics
  • 71. ARRAY OPERATIONS (OR MORE READABLE LOOPS)
  • 72. ARRAY METHODS VS LOOPS const activeItems = []; for (let i = 0; i < arr.length; i++) { if (arr[i].active === true) { activeItems.push(arr[i]); } } // vs const activeItems = arr.filter(item => item.active === true);
  • 73. ARRAY METHODS VS LOOPS Array methods are usually better because: Traversal logic is abstracted Terser, more readable and declarative code Functions and all its goodness!   Loops are better when: Performance is needed (still, very questionable) Need to break out of loop early
  • 74. MAP() Array.prototype.map is a HoF that traverses the list applying the provided operator function to each item and produces a new array with the values returned from each operator call const bananas = [' ', ' ', ' ', ' ', ' ', ' ']; const mix = bananas.map((banana, index) => ( index % 2 === 0 ? ' ' : banana )); console.log(mix); // [' ', ' ', ' ', ' ', ' ', ' ']
  • 76. A WORD ABOUT THE FEARED FUNCTOR In FP terminology, a Functor is a wrapper object that has a utility method for applying an operator function to its wrapped value returning a new Functor wrapping the new value produced by the operator If the wrapped value is compound the Functor applies the operator to each indidual value instead All this is just a fancy way of saying that Functor is just an object that has a map method
  • 77. FILTER() Array.prototype.filter is a HoF that traverses the list applying the provided predicate function to each item and produces a new array with the values of which the predicate function returned truthy const badDiet = [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ']; const goodDiet = badDiet.filter(food => !food.includes(' ')); console.log(goodDiet); // [' ', ' ', ' ', ' ']
  • 79. REDUCE() Array.prototype.reduce is a HoF that traverses the list applying the provided reducer function to the previous returned value and current value And produces whatever the last reducer call returns const people = [' ', ' ', ' ', ' ']; const family = people.reduce((str, person) => ( str === '' ? person : str + 'u200D' + person ), '' /* <- initial value */); console.log(family); // ' '
  • 81. A PRACTICAL INTRO TO FP Some "mandatory" concepts: First Class Functions High-Order Functions & Closures Function Purity Managing Function Input Function Composition Value Immutability Array Operations Recursion Monads Basics
  • 82. RECURSION (OR RECURSION (OR RECURSION...))
  • 83. Recursion is when a function calls itself until a base condition is satisfied const fib = n => n <= 1 ? n : fib(n - 2) + fib(n - 1); fib(10); // 55
  • 84. DECLARATIVE ITERATIONS Although it may be less performant, expressing repetition with recursion is usually more readable because of its declarative nature const sum = (...values) => { let total = 0; for(let i = 0; i < values.length; i++) { total += values[i]; } return total; }; // vs const sum = (firstValue, ...otherValues) => otherValues.length === 0 ? firstValue : firstValue + sum(...otherValues);
  • 85. "Loops may achieve a performance gain for your program. Recursion may achieve a performance gain for your programmer. Choose which is more important in your situation!" - Leigh Caldwell
  • 86. DIVIDE AND CONQUER An common strategy to apply when creating a recursive functions is taking the divide and conquer approach: Treat every list as a pair containing the first value and a list with the rest of the values Define the base condition Define logic around the first value Apply the function itself to the rest of the values
  • 87. DIVIDE AND CONQUER Iterative: Recursive: const map = (arr, fn) => { const newArr = []; for (let i = 0; i < arr.length; i++) { newArr.push(fn(arr[i])); } return newArr; }; const map = ([firstVal, ...rest], fn) => firstVal === undefined ? [] : [fn(firstVal), ...map(rest, fn)];
  • 88. DIVIDE AND CONQUER Note that this technique doesn't only work with lists. Lists are just easier to wrap you head around the concept This approach can be applied to almost anything const iterativeRangeSum = (start, end) => { let result = start; for (let i = start; i < end; i++) { result += i; } return result; } // vs const recursiveRangeSum = (start, end) => start === end ? 0 : start + recursiveRangeSum(start, end - 1);
  • 89. STACK OVERFLOW The function stack is a limited resource If the base condition of a recursive function is not met until the environment runs out of stack frames The program/application will crash and burn Always ensure the base condition will be satisfied before that or refactor the function to use the benefits of tail call optimization // If list has something like 200 items or more, // Stack Overflow! const values = recursiveMap(bigList, item => item.value);
  • 90. TAIL CALL OPTIMIZATION Tail call is when a function call is the very last thing evaluated inside a function When this happens the compiler can optimize the runtime by reusing the last stack frame const foo = () => { const value = 'tail call'; return bar(value); // <- bar is being tail called };
  • 91. TAIL CALL OPTIMIZATION By refactoring the recursive map utility from: To use TCO: map() will now support mapping over lists of any size const map = ([firstVal, ...rest], fn) => firstVal === undefined ? [] : [fn(firstVal), ...map(rest, fn)]; const map = ([firstVal, ...rest], fn, result = []) => firstVal === undefined ? result : map(rest, fn, [...result, fn(firstVal)]);
  • 92. A PRACTICAL INTRO TO FP Some "mandatory" concepts: First Class Functions High-Order Functions & Closures Function Purity Managing Function Input Function Composition Value Immutability Array Operations Recursion Monads Basics
  • 93. MONADS BASICS (OR THE THING THAT, ONCE UNDERSTOOD, YOU WON'T BE ABLE TO EXPLAIN)
  • 94. But first, some boring topics that won't be covered: (but you should at some point) Abstract Algebra Category Theory Type Theory Some people say these fundamental topics are mandatory to start FPing I disaggree. But when you feel ready to dive into the theory behind FP, it's' recommended you do so
  • 95. REVISITING FUNCTORS Meet something and nothing: Note: the names of this Functor implementations vary a lot (Some/None, Just/Nothing, Something/Empty...) That happens because Functors, like any other FP type, is like a loose interface Implementations must respect the type laws but their names are not enforced const something = (value) => ({ map: fn => something(fn(value)) }); const nothing = () => ({ map: nothing });
  • 96. REVISITING FUNCTORS something is useful const getUser = userId => { const user = repository.findUserById(userId); return user ? something(user) : nothing(); } // now we can write // beautiful, idiomatic, declarative code getUser(existingId) // User exists .map(attachPermissions(permissions)) .map(attachOrders(orders)) .map(showProfile(template));
  • 97. REVISITING FUNCTORS nothing is useful It's the same exact code, but nothing happens. Not even an exception! const getUser = userId => { const user = repository.findUserById(userId); return user ? something(user) : nothing(); } // now we can write // beautiful, idiomatic, declarative code getUser(nonExistantId) // User is not found .map(attachPermissions(permissions)) .map(attachOrders(orders)) .map(showProfile(profileTemplate));
  • 98. REVISITING FUNCTORS Maybe? When we use the Something and Nothing Functors together in a function like this, we're actually implementing the Maybe Functor. It allows easier and safer null/empty checks without sacrificing readability.
  • 99. REVISITING FUNCTORS handling branches/errors const success = (value) => ({ map: fn => something(fn(value)), catch: nothing }); const error = (e) => ({ map: () => error(e), catch: fn => something(fn(e)) });
  • 100. REVISITING FUNCTORS handling branches/errors const getUser = userId => { const user = repository.findUserById(userId); return user ? success(user) : error(new Error('User not found')); } // now we can write // beautiful, idiomatic, declarative code getUser(nonExistantId) // User is not found .map(attachPermissions(permissions)) .map(attachOrders(orders)) .map(showProfile(profileTemplate)) // error branch executed when is error .catch(showError(errorTemplate));
  • 101. REVISITING FUNCTORS Either? When we use the Error and Success together in a function like this, we're actually implementing the Either Functor. Its strict implementation is more complex, but the core concept is the same. In the strict implementation of the Either Functor, the Error and Success Functors are o en called Le and Right respectively.
  • 102. REVISITING FUNCTORS Containing containers const attachPermissions = permissions => user => permissions ? something(user.setPermissions(permissions)) : nothing(); const attachOrders = orders => user => orders ? something(user.setOrders(orders)) : nothing(); getUser(nonExistantId) .map(attachPermissions(permissions)) // something(something(user)) .map(attachOrders(orders))// Error: setOrders is not a function .map(showProfile(profileTemplate)) .catch(showError(errorTemplate));
  • 103. MONADS TO THE RESCUE Much like the Functor, the Monad has a utility method for applying an operator function to its wrapped value But unlike the Functors map() utility, it does not wrap the output value in a new monad Because it expects the value to be already wrapped in a monad This utility function has many names: bind(), chain() and flatMap()
  • 104. MONADS TO THE RESCUE Unboxing a box const something = (value) => ({ map: fn => something(fn(value)), flatMap: fn => fn(value) }); const nothing = () => ({ map: nothing, flatMap: nothing });
  • 105. MONADS TO THE RESCUE Unboxing a box const success = (value) => ({ map: fn => something(fn(value)), flatMap: fn => fn(value) catch: nothing }); const error = (e) => ({ map: () => error(e), flatMap: () => error(e), catch: fn => something(fn(e)) });
  • 106. MONADS TO THE RESCUE Unboxing a box const attachPermissions = permissions => user => permissions ? something(user.setPermissions(permissions)) : nothing(); const attachOrders = orders => user => orders ? something(user.setOrders(orders)) : nothing(); getUser(nonExistantId) .flatMap(attachPermissions(permissions))// something(user) .flatMap(attachOrders(orders))// something(user) .map(showProfile(profileTemplate)) .catch(showError(errorTemplate));
  • 107. YOU ARE USING MONADS ALREADY Let's create a fictional monad that wraps a future value That can be used like this: const future = futureValue => ({ map: fn => future(fn(futureValue)), flatMap: fn => fn(futureValue) }); future(asyncGetUser(userId)) .flatMap(asyncAttatchPermissions(userId)) .flatMap(asyncAttatchOrders(userId)) .map(showProfile(profileTemplate));
  • 108. YOU ARE USING MONADS ALREADY But map() and flatMap() are not very meaningful when dealing with future values What if we "merged" and renamed them? const future = futureValue => ({ then: fn => { const nextFutureValue = fn(futureValue); const isFutureMonad = ( nextFurureValue && typeof nextFutureValue.then === 'function' ); return isFutureMonad ? nextFutureValue : future(nextFutureValue); } });
  • 109. YOU ARE USING MONADS ALREADY Now it reads a lot better: future(asyncGetUser(userId)) .then(asyncAttatchPermissions(userId)) .then(asyncAttatchOrders(userId)) .then(showProfile(profileTemplate));
  • 110. YOU ARE USING MONADS ALREADY Feeling a déjà vu? Yes! Promise is a Monad! Promise.resolve(asyncGetUser(userId)) .then(asyncAttatchPermissions(userId)) .then(asyncAttatchOrders(userId)) .then(showProfile(profileTemplate));
  • 112. IMPERATIVE VS DECLARATIVE WHAT THIS CODE IS DOING?
  • 113. const words = [ 'The', 'quick', 'brown', 'fox,', 'jumps', 'over', 'the', 'lazy', 'dog.', '- It', 'was', 'a', 'german', 'shepherd!' ]; let result = false; for (let i = 0; i < words.length; i++) { words[i] = words[i].replace(/[ -_:;.,!?]/g, ''); if (words[i].length >= 3 && words[i].length <= 6) { words[i] = words[i].toUpperCase() .split('').reverse().join(''); if (words[i] === 'GOD') { result = true; break; } } } if (result) console.log('Found GOD...'); if (words[0] !== 'The') console.log('...and the devil');
  • 115. const words = [ 'The', 'quick', 'brown', 'fox,', 'jumps', 'over', 'the', 'lazy', 'dog.', '- It', 'was', 'a', 'german', 'shepherd!' ]; const removeInvalidChars = word => word.replace(/[ -_:;.,!?]/g, ''); const enoughChars = word => word.length >= 3; const canContainGod = word => word.length >= 3 && word.length <= 6; const toUpperCase = word => word.toUpperCase(); const reverseStr = word => word.split('').reverse().join(''); const toBibleCode = compose(reverseStr, toUpperCase); const isGod = word => word === 'GOD'; const logIf = (condition, str) = condition && console.log(str); const result = words .map(removeInvalidChars) .filter(canContainGod) .map(toBibleCode) .some(isGod); logIf(result === true, 'Found GOD...'); logIf(words[0] !== 'The', '...and the devil');
  • 116. ANSWER: (ALMOST) THE SAME THING Challenge: Can you spot a difference between both outputs?
  • 119. JS FP LIBS - Functional utilities on top of lodash - Functional utilities - Functional utilities - Suite of functional libraries lodash/fp Ramda functional.js Folktale
  • 120. RESOURCES - Great FP book, greatly inspired this talk - Awesome FP book, dives into theory more - A must read for every JS developer - List of resources about FP in JS Functional Light JS (by getify) Mostly adequate guide to FP JavaScript Allongé Awesome FP JS