Why to FP?

In general, a more reliable & maintainable code in the long term

Because FP "laws" and tools enables us to write a more:

But the journey has its bumps

We'll go over some "mandatory" concepts:

Which means they can:

The majority of patterns and tools around FP requires functions to be treated as first-class citizens

First Class Functions (or functions as values)

In other words, it's a function that "remembers" the state/environment where it was created

A closure is a function that refers to "free variables" (variables defined in parent scopes)

Useful to "hide" state (achieve privacy), persist state to be processed/used later and compose/add behaviour to other functions

Useful to separate concerns and abstract/decouple logic

A high-order function is a function that does at least one of the following:

most of a program codebase should be composed of small, single-purpose and pure functions

as they make programs hard to follow/read, hard to test and hard to maintain

but, side effects should be avoided where possible

A program without any observable side effect is also a program that accomplishes nothing useful

A function is considered pure if it does not break the following "laws":

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 const double = n => n * 2; // arity = 1 (unary) // arity = 3 (ternary) const getFullName = (firstName, middleName, lastName) => `${firstName} ${middleName} ${lastName}`; It's possible to get a function's arity through the Function.prototype.length property 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 const strArr = ['1', '2', '3', '4', '5']; const mumArr = strArr.map(parseInt); console.log(numArr); // [1, NaN, NaN, NaN, NaN] 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)

Forcing Unary Functions We can fix that with a utility HoF usually called unary That can be implemented in JS like so: const unary = fn => param => fn(param); And used like this: 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: const partial = (fn, ...eagerArgs) => (...lazyArgs) => fn(...eagerArgs, ...lazyArgs); And it's used like this: 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: const partialRight = (fn, ...rightArgs) => (...leftArgs) => fn(...leftArgs, ...rightArgs); That can be used like this: 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