-
Watch course
Functional programming is a coding paradigm inspired by the "Lambda Calculus"
The lambda functions are uni arity functions, therefore, most of our functions will be defined this way.
Abstraction
Inheritance
Polymorphism
Encapsulation
Purity
Recursion
Closures
Immutability
Imperative
Declarative
Functional
OOP
Follow don't think
do this then do that "as I say".
const findMismatchingElement = (needle, haystack) => {
for (let i = 0; i < needle.length; i++) {
if (haystack.indexOf(needle[i]) === -1) return [needle[i]]
}
return []
}
Find out your way of doing my task
I want to do that, "do it your way"
const findMismatchingElement = (needle, haystack) =>
needle.filter((item) => haystack.indexOf(item) === -1)
A function is an I/O process of data
A function is an I/O process of data
Triangulate
A function is an I/O process of data
const sum = (...numbers) => numbers.reduce((val, accum) => val + accum, 0)
Sum
Numbers
A procedure is set of instructions performed on some sort of data, more likely an input
function addNumbers(...numbers) {
let total = 0
for (number of numbers) {
total += number
}
console.log(total)
}
A pure function is a function that does not interact with the outside world i.e. global scope
A pure function is a function that does not interact with the outside world i.e. global scope
Impure
let first_name = 'Ahmed';
const greeting = () => console.log(`Hello ${first_name}`);
Pure
const greeting = (first_name) => `Hello ${first_name}`;
A pure function is a function that does not interact with the outside world i.e. global scope
Impure
const coords = { x: 10, y: 5 };
const increaseHorizontalCoordinate = (amount) => {
coords.x += amount;
};
console.log(coords);
increaseHorizontalCoordinate(2);
console.log(coords);
Pure
const coords = { x: 10, y: 5 };
const increaseHorizontalCoordinate = (coords, amount) => {
return {
x: coords.x + amount,
y: coords.y,
};
};
console.log(coords);
console.log(increaseHorizontalCoordinate(coords, 2));
let tweet = {
user: {
id: '119C9AE6F9CA741BD0A76F87FBA0B22CAB5413187AFB2906AA2875C38E213603',
name: 'Prof. James Moriarty',
},
message: 'In world of locked rooms, the man with the key is a KING',
}
const makeTweet = (tweet) => {
let record = {
id: generateIdFor(tweet),
userID: tweet.user.id,
twitter: tweet.user.name,
message: tweet.message,
time: Date.now(),
}
let elem = buildTweetElement(record)
tweetsList.appendChild(elem)
}
Impure
let tweet = {
user: {
id: '119C9AE6F9CA741BD0A76F87FBA0B22CAB5413187AFB2906AA2875C38E213603',
name: 'Prof. James Moriarty',
},
message: 'In world of locked rooms, the man with the key is a KING',
}
const makeTweet = (tweet, id) => ({
id,
userID: tweet.user.id,
twitter: tweet.user.name,
message: tweet.message,
time: Date.now(),
})
const addTweet = (tweet) => {
tweetsList.appendChild(
buildTweetElement(makeTweet(tweet, generateIdFor(tweet)))
)
}
Pure
(HOFS)
Functions in JavaScript are considered first-class citizens which enables them to be
(HOFS)
function loopAndDouble(array) {
for (let i = 0; i < array.length; i++) {
array[i] *= 2;
}
}
function loopAndHalfen(array) {
for (let i = 0; i < array.length; i++) {
array[i] /= 2;
}
}
function loopAndAddTwo(array) {
for (let i = 0; i < array.length; i++) {
array[i] += 2;
}
}
function loopAndSubtractTwo(array) {
for (let i = 0; i < array.length; i++) {
array[i] -= 2;
}
}
Besides the fact that these functions are completely repetitive, they're modifying the main array which causes impurity.
(HOFS)
const double = (n) => n * 2
const halfen = (n) => n / 2
let doubled = applyRule([1, 2, 3], double) // [2, 4, 6]
let halfed = applyRule([1, 2, 3], halfen) // [0.5, 2, 1.5]
const applyRule = (array, rule) => array.map(rule)
This refactor guarantees you not repeating your code (applying DRY) and a lot more concise, pure and declarative code
Arguments and parameters are always used interchangeably
The difference between them is that argument is the value passed to a function on calling this function
const add = (x, y) => x + y
let z = add(2, 3)
Parameter
Argument
Arguments do shape the function
const add = (x, y) => x + y
let z = add(2, 3)
The add function is called binary function
const add = (x, y) => x + y
const increase = (x) => add(x, 1)
The increase function is called unary function
const unary = (fn) => (arg) => fn(arg)
const binary = (fn) => (arg1, arg2) => fn(arg1, arg2)
const flip = (fn) => (arg1, arg2, ...args) => fn(arg2, arg1, ...args)
const reverse = (fn) => (...args) => fn(...args.reverse())
const apply = (fn) => (...args) => fn(...args)
const f = (...args) => args
doSomething(function operation(input) {
return predicate(input)
})
doSomething(function predicate(input) {
return operation(input)
})
Functions of the same shape, are interchangeable
Point-free functions: defining a function without defining its points (aka inputs)
So what are point-free functions?
const isEven = (number) => (number % 2 == 0)
const isOdd = (number) => (number % 2 == 1)
const isOdd = number => !isEven(number)
These functions are all not point-free functions
as they are defining their inputs
(HOFS)
Functions in JavaScript are considered first-class citizens which enables them to be
(HOFS)
function loopAndDouble(array) {
for (let i = 0; i < array.length; i++) {
array[i] *= 2;
}
}
function loopAndHalfen(array) {
for (let i = 0; i < array.length; i++) {
array[i] /= 2;
}
}
function loopAndAddTwo(array) {
for (let i = 0; i < array.length; i++) {
array[i] += 2;
}
}
function loopAndSubtractTwo(array) {
for (let i = 0; i < array.length; i++) {
array[i] -= 2;
}
}
Besides the fact that these functions are completely repetitive, they're modifying the main array which causes impurity.
(HOFS)
const double = (n) => n * 2
const halfen = (n) => n / 2
let doubled = map([1, 2, 3], double) // [2, 4, 6]
let halfed = map([1, 2, 3], halfen) // [0.5, 2, 1.5]
const map = (array, fn) => array.map(fn)
This refactor guarantees you not repeating your code (applying DRY) and a lot more concise, pure and declarative code
const double = (n) => n * 2
const halfen = (n) => n / 2
let doubled = map([1, 2, 3], double) // [2, 4, 6]
let halfed = map([1, 2, 3], halfen) // [0.5, 2, 1.5]
const map = (array, fn) => array.map(fn)
The map function under the hood
function map(array, fn) {
let copy = []
for (let i = 0; i < array.length; i++) {
copy.push(fn(array[i]))
}
return copy
}
function length(array) {
return array.length
}
function head(array) {
return array[0]
}
function concat(array1, array2) {
return array1.concat(array2)
}
function tail(array) {
return array.slice(1)
}
const map = (array, fn) => array.map(fn)
The map function under the hood
function map(array, fn) {
let copy = []
for (let i = 0; i < array.length; i++) {
copy.push(fn(array[i]))
}
return copy
}
function map(array, fn) {
if (length(array) === 0) return []
return [fn(head(array))].concat(map(tail(array), fn))
}
const map = (array, fn) => array.map(fn)
The map function under the hood
function filter(array, fn) {
let copy = []
for (let i = 0; i < array.length; i++) {
if (fn(array[i])) {
copy.push(array[i])
}
}
return copy
}
const filter = (array, fn) => array.filter(fn)
The filter function under the hood
function filter(array, predicateFn) {
if (length(array) === 0) return []
const firstItem = head(array)
const filteredFirst = predicateFn(firstItem) ? [firstItem] : []
return concat(filteredFirst, filter(tail(array), predicateFn))
}
const filter = (array, fn) => array.filter(fn)
The filter function under the hood
function reduce(arr, reducer, initialValue) {
for (let i = 0; i < arr.length; i++) {
initialValue = reducer(initialValue, arr[i])
}
return initialValue
}
const reduce = (array, fn, initialValue) => array.reduce(fn, initialValue)
The reduce function under the hood
function reduce(array, reducer, initial) {
if (length(array) === 0) return initial
const newInitialValue = reducer(initial, head(array))
return reduce(tail(array), reducer, newInitialValue)
}
const reduce = (array, fn, initialValue) => array.reduce(fn, initialValue)
The reduce function under the hood
function mapReduce(array, fn) {
let copy = []
for (let i = 0; i < array.length; i++) {
copy.push(fn(array[i]))
}
return function reduce(reducer, initialValue, array = copy) {
let accumulator = initialValue === undefined ? 0 : initialValue
for (let i = 0; i < array.length; i++)
accumulator = reducer(accumulator, array[i], i, array)
return accumulator
}
}
const value = [1,2,3,4].map(x => x * 2).reduce((a, b) => a + b, 0)
The mapReduce function under the hood
function mapReduce(array, fn) {
if (length(array) === 0) return []
let arr = [fn(head(array))].concat(map(tail(array), fn))
return function reduce(reducer, initial) {
function apply(x, y, array = arr) {
if (length(array) == 0) return y
const newInitialValue = x(y, head(array))
return apply(reducer, newInitialValue, tail(array))
}
return apply(reducer, initial)
}
}
const value = [1,2,3,4].map(x => x * 2).reduce((a, b) => a + b, 0)
The mapReduce function under the hood
A scope is the variables that a function can access at certian line ( at function call).
Global scope is when you define a variable appended to the window object
Global scope is when you define a variable appended to the window object
var first_name = "Ahmed"
Script scope is when you define a variable that's accessible everywhere but not appended to the window object
var first_name = "Ahmed"
let last_name = "Osama"
Local scope is when you define a variable inside a functional scope
function one() {
let x = 1
function two() {
let x = 2
function three() {
let x = 3
}
}
}
Block scope is when you define a variable within any curly braces or a statement on condition that you use a let instead of var
for (var i = 0; i < 5; i++) {
console.log(`i value now is: ${i}`);
}
console.log(`i value now is: ${i}`)
...
for (var i = 0; i < 5; i++) {
console.log(`i value now is: ${i}`);
}
console.log(`i value now is: ${i}`)
for (let j = 0; j < 5; i++) {
console.log(`j value now is: ${j}`);
}
console.log(j) // reference error
Block scope is when you define a variable within any curly braces or a statement on condition that you use a let instead of var
if (true) {
var x = 5;
}
console.log(x); // 5
...
Block scope is when you define a variable within any curly braces or a statement on condition that you use a let instead of var
if (true) {
var x = 5;
}
console.log(x); // 5
if (false) {
// some dummy code
} else {
const y = 5;
}
console.log(y); // reference error
Block scope is when you define a variable within any curly braces or a statement on condition that you use a let instead of var
{
var x = 5
}
console.log(x) // 5
...
Block scope is when you define a variable within any curly braces or a statement on condition that you use a let instead of var
{
var x = 5
}
console.log(x) // 5
{
let y = 5;
}
console.log(y) // reference error
Block scope is when you define a variable within any curly braces or a statement on condition that you use a let instead of var
let x = 5
function firstLayer() {
console.log(x)
...
}
firstLayer()
Lexical scope is when a group of nested functions have access to their defined variables and the variables that are defined in their parent scope.
let x = 5
function firstLayer() {
console.log(x)
let y = 3
return function secondLayer() {
console.log(y)
...
}
}
firstLayer()()
Lexical scope is when a group of nested functions have access to their defined variables and the variables that are defined in their parent scope.
Lexical Scope == [[Scopes]]
function counter() {
...
}
let x = counter()
function counter() {
var initial = 0
var final = 5
...
}
let x = counter()
function counter() {
var initial = 0
var final = 5
var increase = function () {
return initial++
}
return increase
}
let x = counter()
function counter() {
var initial = 0
var final = 5
return function increase() {
return initial++
}
}
let x = counter()
function counter() {
var initial = 0
var final = 5
var increase = () => initial++;
var decrease = () => initial--;
return {
increase,
decrease
}
}
let x = counter()
Kyle Simpson
Closure is when a function "remembers" its lexical scope even when the function is executed outside that lexical scope.
function counter() {
var idx = 1
return function one() {
let idx1 = 2
return function two() {
let idx2 = 3
return idx + idx1 + idx2
}
}
}
Currying
Partial Apps
Encapsulation
Stateful function
Memoization
Shaping functions
Module pattern
Partial applications means that you transform a function from being general to special by partially applying some parameters.
Optimizing Recursion
Mocking classes
Currying
Encapsulation
Stateful function
Memoization
Shaping functions
Module pattern
Partial Apps
var list = (joint, ...items) => {
...
}
list('and', 'one', 'two', 'three') // one, two, and three
list('or', 'one', 'two') // one or two
Partial applications means that you transform a function from being general to special by partially applying some parameters.
Mocking classes
Optimizing Recursion
Currying
Encapsulation
Stateful function
Memoization
Shaping functions
Module pattern
Partial Apps
Partial applications means that you transform a function from being general to special by partially applying some parameters.
var list = (joint, ...items) => {
var commaSeparated = items.slice(0, -1).join(', ')
var lastItem = items.pop()
return `${commaSeparated} ${joint} ${lastItem}`
}
list('and', 'one', 'two', 'three') // one, two, and three
list('or', 'one', 'two') // one or two
Mocking classes
Optimizing Recursion
Currying
Encapsulation
Stateful function
Memoization
Shaping functions
Module pattern
Partial Apps
Partial applications means that you transform a function from being general to special by partially applying some parameters.
var list = (joint, ...items) => {...}
function partial(fn, ...args) {
return function partiallyAppliedArgs(...rest) {
return fn(...args, ...rest)
}
}
list('and', 'one', 'two', 'three') // one, two and three
list('or', 'one', 'two') // one or two
var listAnd = partial(list, 'and') // function partiallyAppliedArgs(...rest) {}
listAnd('five', 'six', 'seven') // five, six and seven
Mocking classes
Optimizing Recursion
Currying
Encapsulation
Stateful function
Memoization
Shaping functions
Module pattern
Partial Apps
function multiply(x, y) {
return x * y;
}
function pow(base, exp) {
return base ** exp;
}
Currying is the action of reducing functions arity from enary to unary by producing newer functions with prefilled inputs.
Mocking classes
Optimizing Recursion
Currying
Encapsulation
Stateful function
Memoization
Shaping functions
Module pattern
function multiply(x, y) {
return x * y;
}
function pow(base, exp) {
return base ** exp;
}
Currying is the action of reducing functions arity from enary to unary by producing newer functions with prefilled inputs.
General functions
Mocking classes
Optimizing Recursion
Currying
Encapsulation
Stateful function
Memoization
Shaping functions
Module pattern
function multiply(x, y) {...}
function pow(base, exp) {...}
var double = (x) => mutliply(2, x)
var square = (x) => pow(x, 2)
Currying is the action of reducing functions arity from enary to unary by producing newer functions with prefilled inputs.
General functions
Mocking classes
Optimizing Recursion
Currying
Encapsulation
Stateful function
Memoization
Shaping functions
Module pattern
function mutliply(y) {
return function multiplyFor(x) {
return x * y;
}
}
function pow(exponent) {
return function powFor(base) {
return base ** exponent;
}
}
Currying is the action of reducing functions arity from enary to unary by producing newer functions with prefilled inputs.
General functions
Mocking classes
Optimizing Recursion
Currying
Encapsulation
Stateful function
Memoization
Shaping functions
Module pattern
function mod(y) {
return function modFor(x) {
return x % y;
}
}
function eq(y) {
return function eqTo(x) {
return x == y;
}
}
Currying is the action of reducing functions arity from enary to unary by producing newer functions with prefilled inputs.
Mocking classes
Optimizing Recursion
Currying
Encapsulation
Stateful function
Memoization
Shaping functions
Module pattern
const R = require('ramda')
var list = (joint, items) =>
`${items.slice(0, -1).join(', ')} ${joint} ${items.pop()}`
var curriedList = R.curry(list)
var orList = curriedList('or')
var andList = curriedList('and')
console.log(curriedList('or', ['one', 'two', 'three'])) // one, two or three
console.log(orList(['one', 'two'])) // one or two
console.log(andList(['one', 'two'])) // one and two
console.log(andList(['one', 'two', 'three', 'four'])) // one, two, three and four
Currying is the action of reducing functions arity from enary to unary by producing newer functions with prefilled inputs.
Mocking classes
Optimizing Recursion
Currying
Encapsulation
Stateful function
Memoization
Shaping functions
Module pattern
Encapsulation is the act of hiding state and make it only accessible to class memebers.
Mocking classes
class Encapsulation {
#state = []
}
var enc = new Encapsulation()
console.log(enc.state)
/* index.js
* returns undefined not an empty
* array as state is completely
* different from the private
* member #state
*/
class Encapsulate {
private state: any = []
}
var enc = new Encapsulate()
console.log(enc.state)
/* index.ts
* Throws an error as we cannot
* access a private property outside
* its context
*/
Optimizing Recursion
Encapsulation
Stateful function
Memoization
Shaping functions
Module pattern
Encapsulation is the act of hiding state and make it only accessible to class memebers.
Mocking classes
class Encapsulate {
#state = null
constructor(state) {
this.#state = state
}
get state() {
return this.#state
}
}
var enc = new Encapsulate([])
console.log(enc.state) // []
class Encapsulate {
private _state: any
constructor(state: any) {
this._state = state
}
get state() {
return this._state
}
}
var enc = new Encapsulate([])
console.log(enc.state) // []
Optimizing Recursion
Encapsulation
Stateful function
Memoization
Shaping functions
Module pattern
Encapsulation is the act of hiding state and make it only accessible to class memebers.
Mocking classes
function encapsulation(state = null) {
return {
get state() {
return state
},
}
}
var enc = encapsulation([])
console.log(enc.state)
function ecnapsulation(state: any = null) {
return {
get state() {
return state
},
}
}
var enc = ecnapsulation([])
console.log(enc.state)
Optimizing Recursion
Encapsulation
Stateful function
Memoization
Shaping functions
Module pattern
Mocking classes
Lazy/Eager Execution
function repeat(count) {
return function generateString(string) {
return string.repeat(count)
}
}
let word = repeat(10)
word("a ") // "a a a a a a a a a a "
word("a ") // "a a a a a a a a a a "
Optimizing Recursion
Stateful function
Memoization
Shaping functions
Module pattern
Mocking classes
Lazy/Eager Execution
function repeat(count) {
var str = "a ".repeat(count)
return function generateString() {
return str
}
}
let word = repeat(10)
word() // "a a a a a a a a a a "
word() // "a a a a a a a a a a "
Optimizing Recursion
Stateful function
Memoization
Shaping functions
Module pattern
Mocking classes
Lazy/Eager Execution
function repeat(count) {
var str
return function generateString(string) {
if (typeof str == 'undefined') {
str = string.repeat(count)
}
return str
}
}
let word = repeat(10)
word('a ') // "a a a a a a a a a a "
word('a ') // "a a a a a a a a a a "
Optimizing Recursion
Stateful function
Memoization
Shaping functions
Module pattern
Mocking classes
function repeat(count) {
return R.memoizeWith(function generateString(string) {
return string.repeat(count)
})
}
let word = repeat(10)
word('a ') // "a a a a a a a a a a "
word('a ') // "a a a a a a a a a a "
Optimizing Recursion
Stateful function
Memoization
Shaping functions
Module pattern
Mocking classes
;[1, 2, 3].map(increase)
function add(x, y) {
return x + y
}
function increase(number) {
return add(1, number)
}
Optimizing Recursion
Stateful function
Memoization
Shaping functions
Module pattern
Mocking classes
const R = require('ramda')
var add = R.curry(function add(x, y) {
return x + y
})
;[1, 2, 3].map(add(1))
Optimizing Recursion
function reverse(fn) {
return function (...args) {
return fn(...args.reverse())
}
}
function apply(fn) {
return function (...args) {
return fn(...args)
}
}
Stateful function
Memoization
Shaping functions
Module pattern
Mocking classes
function unary(fn) {
return function one(arg) {
return fn(arg)
}
}
function binary(fn) {
return function two(arg1, arg2) {
return fn(arg1, arg2)
}
}
function reverse(fn) {
return function (...args) {
return fn(...args.reverse())
}
}
function apply(fn) {
return function (...args) {
return fn(...args)
}
}
Optimizing Recursion
Shaping functions
Module pattern
Mocking classes
var counter = (function (initial) {
return { increase }
function increase() {
return ++initial
}
})(0)
function reverse(fn) {
return function (...args) {
return fn(...args.reverse())
}
}
function apply(fn) {
return function (...args) {
return fn(...args)
}
}
Optimizing Recursion
Module pattern
Mocking classes
class Counter {
constructor(initial) {
this.initial = initial
}
increase(step = 1) {
return (this.initial += step)
}
decrease(step = 1) {
return (this.initial -= step)
}
}
Optimizing Recursion
Module pattern
Mocking classes
var counter = (function counter(initial = 0) {
return { increase, decrease }
function increase(step = 1) {
return (initial += step)
}
function decrease(step = 1) {
return (initial -= step)
}
})()
Optimizing Recursion
Unix philosophy
Unix philosophy
Ken Thompson
Make each program do one thing well. To do a new job, build afresh rather than complicate old programs by adding new “features.”
Expect the output of every program to become the input to another, as yet unknown, program.
Don't clutter output with extraneous information.
Avoid stringently columnar or binary input formats.
Don't insist on interactive input.
Function composition is a mathematical term where two functions are called successively, the first function's output is passed as an input to the second function.
Unix philosophy
Function composition is a mathematical term where two functions are called successively, the first function's output is passed as an input to the second function.
Function composition is a mathematical term where two functions are called successively, the first function's output is passed as an input to the second function.
Function composition is a mathematical term where two functions are called successively, the first function's output is passed as an input to the second function.
import axios from 'axios'
const { data } = await axios.get('http://jsonplaceholder.typicode.com/todos')
const ab = data
.slice(0, 5)
.filter((todo) => todo.completed)
.map((todo) => todo.title)
console.log(ab)
Function composition, nesting functions may be known to you as function chaining where.
import axios from 'axios'
import R from 'ramda'
const { data } = await axios.get('http://jsonplaceholder.typicode.com/todos')
const collectTitles = R.compose(
R.map((todo) => todo.title),
R.filter((todo) => todo.completed),
R.slice(0, 5)
)
console.log(collectTitles(data))
Function composition, nesting functions may be known to you as function chaining where.
function pipe(...fns) {...}
function compose(...fns) {...}
/* What's the difference between piping and composing? */
Function composition, nesting functions may be known to you as function chaining where.
function compose(...fns) {...}
const add = (x) => x + 1
const double = (x) => x * 2
const decrease = (x) => x - 1
// compose(add, double, decrease) == compose(compose(add, double), decrease)
// == compose(add, compose(double, decrease))
import R from 'ramda'
const modulo = R.curry((y) => (x) => x % y)
const isOdd = R.compose(R.equals(1), modulo(2))
import axios from 'axios'
import R from 'ramda'
const { data } = await axios.get('http://jsonplaceholder.typicode.com/todos')
const collectTitles = R.compose(
R.map((todo) => todo.title),
R.filter((todo) => todo.completed),
R.slice(0, 5)
)
console.log(collectTitles(data))
import R from 'ramda'
const modulo = R.curry((y) => (x) => x % y)
const isOdd = R.compose(R.equals(1), modulo(2))
A recursive function is a function that calls itself within its inner scope
but given a smaller input than the original one passed
function inception() {
inception();
}
// inception();
Recursive functions can consume whole a lot of memory and much much time to execute some tasks
Also can be tricky concept to grab your head around at first.
Last but not least, its syntax may seem awkard to anyone.
Although recursion can be a pain in the ass, but it comes with whole lot pros
Back to our lovely inception() function
debugging this function shows a huge relation between recursion and call stack
function inception() {
inception();
}
// inception();
Thus the recursion consumes all the memory we have in our system and causes stack over flow
Base case
Recursion case
The case at which the recursion stops and returns its value to the previous function call
The case at which the recursive function is recursing, till it reaches the base case and exits
Execution context
Execution context
Execution context
Execution context
Global Execution Context
Call stack
Execution context
Execution context
Execution context
Execution context
Global Execution Context
Call stack
Execution context
Execution context
Execution context
Execution context
Global Execution Context
Call stack
factorial(5)
factorial(4)
factorial(3)
factorial(2)
factorial(1)
5
*
3
*
4
*
2
*
Execution context
Execution context
Execution context
Execution context
Global Execution Context
Call stack
factorial(5)
factorial(4)
factorial(3)
factorial(2)
factorial(1)
5
*
3
*
4
*
2
*
1
2
6
24
120
As mentioned earlier, recursion can consume a lot of memory and takes long time to complete a task
which makes our programs much slower
So, how to improve that mess?
Luckily, recursion can be optimized using Tail Call Optimizations (TCO)
PTC
STC
Trampolines
Trampolines
function trampoline(fn) {
return function (...args) {
let result = fn(...args)
while (typeof result == 'function') {
result = result()
}
return result
}
}
the state of not changing, or being unable to be changed
const user = {
id: 42,
full_name: { first_name: 'ahmed', last_name: 'osama' },
likes: [{}],
comments: [{}],
posts: [{}],
}
doSomethingEvil(user) // May modify user?
dbEntity.save(user) // ?????
const user = {
id: 42,
full_name: { first_name: 'ahmed', last_name: 'osama' },
likes: [{}],
comments: [{}],
posts: [{}],
}
function doSomethingEvil(obj) {
obj.id = undefined
}
doSomethingEvil(user)
dbEntity.save(user) // Error
the state of not changing, or being unable to be changed
var x = 3
let taxRate = 0.3
const PI = 3.14
var first_name = 'ahmed'
Const is some how mutable ^^
const PI = 3.14
const arr = [1,2,3]
const todo = {
task: "Go to sleep",
status: "delayed"
}
const config = Object.freeze({
db: {},
mail: {},
})
const config = Object.freeze({
db: {
username: "root"
},
mail: {},
})
function connect(config) {
config.db.username = "ahmed"
return config
}
const config = Object.freeze({
db: {
username: 'root',
},
mail: {},
})
function connect(config) {
return {
...config,
db: {
username: 'ahmed',
},
}
}
console.log(config)
console.log(connect(config))
console.log(config)
Me, 👉👈
A functor is anything that is mappable
let numbers = [1, 2, 3, 4, 5]
let names = ['ahmed', 'mohamed', 'mahmoud']
let todos = [
{
id: 42,
task: 'Record screen casts',
completed: true,
},
{
id: 43,
task: 'Have a walk',
completed: false,
},
]
Me, 👉👈
A functor is anything that is mappable
function objectMapper(mapper, object) {
let copy = {}
for (let key of Object.keys(object)) {
copy[key] = mapper(object[key])
}
return copy
}
Object.prototype.map = function(mapper) {
let copy = {}
for (let key of Object.keys(this)) {
copy[key] = mapper(this[key])
}
return copy
}
function objectMapper(mapper, object) {
let copy = {}
for (let key of Object.keys(object)) {
copy[key] = mapper(object[key])
}
return copy
}
Object.prototype.map = function(mapper) {
let copy = {}
for (let key of Object.keys(this)) {
copy[key] = mapper(this[key])
}
return copy
}
Me, 👉👈
A functor is anything that is mappable
Me, 👉👈
A functor is anything that is mappable
function Container(value) {
this.value = value
}
Me, 👉👈
A functor is anything that is mappable
function Container(value) {
this.value = value
}
let functor = new Container(5)
let anotherFunctor = new Container({name: 'ahmed'})
Me, 👉👈
A functor is anything that is mappable
function Container(value) {
this.value = value
}
let functor = new Container(5)
let anotherFunctor = new Container([
{
userId: 1,
id: 1,
title:
'sunt aut facere repellat provident occaecati excepturi optio reprehenderit',
body:
'Lorem ipsum dolor, sit amet consectetur adipisicing elit. A quo voluptatibus',
},
])
Me, 👉👈
A functor is anything that is mappable
function Container(value) {
this.value = value
}
Container.prototype.map = function (mapper) {
return Container.of(mapper(this.value))
}
Me, 👉👈
A functor is anything that is mappable
function Container(value) {
this.value = value
}
Container.prototype.map = function (mapper) {
return Container.of(mapper(this.value))
}
Container.of = function (value) {
return new Container(value)
}
Me, uwu
A functor is anything that is mappable
function map(array, mapper) {
return array.map(mapper)
}
function fitler(array, predicate) {
return array.filter(predicate)
}
function reduce(reducer, initialValue, array) {
return array.reduce(reducer, initialValue)
}
function map(mapper, array) {
return array.reduce(
(list, item) => (list.push(mapper(item)), list),
[]
)
}
function filter(predicate, array) {
return array.reduce(
(list, item) =>
predicate(item) ? (list.push(item), list) : list,
[]
)
}
function reduce(reducer, initialValue, array) {
return array.reduce(reducer, initialValue)
}
function unique(array) {
let uniques = new Set()
for (let item of array) {
uniques.add(item)
}
return uniques
}
function unique(array) {
return array.filter(function (item, index) {
return array.indexOf(item) === index
})
}
function unique(array) {
return array.reduce(function (list, item) {
return list.indexOf(item) == -1
? (list.push(item), list)
: list
}, [])
}
function uniqueMap(mapper, array) {
let uniques = new Set()
for (let item of array) {
uniques.add(mapper(item))
}
return uniques
}
function uniqueMap(mapper, array) {
return array.reduce(function (list, item) {
return list.indexOf(item) == -1
? (list.push(mapper(item)), list)
: list
}, [])
}
function uniqueMap(mapper, array) {
return array.reduce(function (list, item) {
return list.add(mapper(item))
}, new Set())
}
function flatten(array, depth = Math.min()) {
let result = [];
array.forEach((item) => {
if (!Array.isArray(item)) {
result.push(item);
} else if (depth === 1) {
result = result.concat(item);
} else {
result = result.concat(flatten(item, depth - 1));
}
});
return result;
}
var flatten = (arr, depth = Infinity) =>
arr.reduce(
(list, v) =>
list.concat(
depth > 0
? depth > 1 && Array.isArray(v)
? flatten(v, depth - 1)
: v
: [v]
),
[]
)
var flatMap = (mapperFn, arr) =>
flatten(arr.map(mapperFn), 1)
var flatMap = (mapperFn, arr) =>
arr.reduce((list, v) => list.concat(mapperFn(v)), [])
function zip(array1, array2) {
var zipped = []
let arr1 = [...array1]
let arr2 = [...array2]
while (arr1.length > 0 && arr2.length > 0) {
zipped.push([arr1.shift(), arr2.shift()])
}
return zipped
}
function zip(array1, array2) {
return array1.map(function makeTuple(item, index) {
return [item, array2[index]]
})
}
function merge(array1, array2) {
let merged = []
let arr1 = [...array1]
let arr2 = [...array2]
while (arr1.length > 0 && arr2.length > 0) {
if (arr1.length > 0) merged.push(arr1.shift())
if (arr2.length > 0) merged.push(arr2.shift())
}
return merged.sort((a, b) => a - b)
}
function isEven(number) {
return number % 2 == 0
}
function double(number) {
return number * 2
}
function add(x, y) {
return x + y
}
;[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
.filter(isEven)
.map(double)
.reduce(add) // 84
const isEven = (x) => x % 2 == 0
const double = (x) => x * 2
const add = (x, y) => x + y
const filter = (predicate, array) => array.filter(predicate)
const map = (mapper, array) => array.map(mapper)
const reduce = (reducer, accum, array) =>
array.reduce(reducer, accum)
reduce(
add,
0,
map(
double,
filter(isEven, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12])
)
) // 84
Not composable
const R = require('ramda')
const isEven = (x) => x % 2 == 0
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
const transducer = R.compose(
R.filter(isEven),
R.map(R.multiply(2))
)
R.transduce(transducer, R.add, 0, numbers) // 84
let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
const double = (x) => x * 2
const add = (x, y) => x + y
const isEven = (x) => x % 2 == 0
numbers.reduce(function RemoveOddsThenDoubleAndSum(
total,
current
) {
if (isEven(current)) {
total = add(total, double(current))
}
return total
},
0)
function map(mapper, array) {
return array.reduce(
(list, item) => (list.push(mapper(item)), list),
[]
)
}
...
function map(mapper, array) {
return array.reduce(
(list, item) => (list.push(mapper(item)), list),
[]
)
}
function map(mapper) {
return function reducer(list, item) {
return list.concat(mapper(item))
}
}
function filter(predicate, array) {
return array.reduce(
(list, item) =>
predicate(item) ? (list.push(item), list) : list,
[]
)
}
function filter(predicate) {
return function reducer(list, item) {
return predicate(item) ? list.concat(item) : list
}
}
function map(mapper) {
return function reducer(list, item) {
return list.concat(mapper(item))
}
}
function filter(predicate) {
return function reducer(list, item) {
return predicate(item) ? list.concat(item) : list
}
}
function map(mappingFn) {
return function combiner(combineFn) {
return function reducer(list, v) {
return combineFn(list, mappingFn(v))
}
}
}
function filter(predicateFn) {
return function combiner(combineFn) {
return function reducer(list, v) {
if (predicateFn(v)) return combineFn(list, v)
return list
}
}
}
function compose(...fns) {
return function composed(v) {
return fns.reduceRight(function reduceBy(accum, fn) {
return fn(accum)
}, v)
}
}
let transducer = compose(
map((x) => x),
filter(isEven),
)
function map(mappingFn) {
return function combiner(combineFn) {
return function reducer(list, v) {
return combineFn(list, mappingFn(v))
}
}
}
function filter(predicateFn) {
return function combiner(combineFn) {
return function reducer(list, v) {
if (predicateFn(v)) return combineFn(list, v)
return list
}
}
}
function compose(...fns) {
return function composed(v) {
return fns.reduceRight(function reduceBy(accum, fn) {
return fn(accum)
}, v)
}
}
let transducer = compose(
map((x) => x),
filter(isEven),
)
function map(mappingFn) {
return function combiner(combineFn) {
return function reducer(list, v) {
return combineFn(list, mappingFn(v))
}
}
}
function filter(predicateFn) {
return function combiner(combineFn) {
return function reducer(list, v) {
if (predicateFn(v)) return combineFn(list, v)
return list
}
}
}
function compose(...fns) {
return function composed(v) {
return fns.reduceRight(function reduceBy(accum, fn) {
return fn(accum)
}, v)
}
}
let transducer = compose(
map((x) => x),
filter(isEven),
)
let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
console.log(numbers.reduce(transducer(add), 0))
Raise up your techinal skills