Jeremy Fairbank
May 05, 2017
340

# Codestock 2017: Functional Programming Basics in ES6

May 05, 2017

## Transcript

2. ### Software is broken. We are here to fix it. Say

[email protected]

14. ### let age = 10; age = 11; const name =

'Tucker'; name = 'Sally'; All good Syntax error
15. ### const add = (x, y) => { return x +

y; }; const identity = x => x;
16. ### const add = (x, y) => { return x +

y; }; const identity = x => x;
17. ### const add = (x, y) => { return x +

y; }; const identity = x => x;
18. ### const add = (x, y) => { return x +

y; }; const identity = x => x;
19. ### const add = (x, y) => { return x +

y; }; const identity = x => x;
20. ### const add = (x, y) => { return x +

y; }; const identity = x => x;
21. ### const array = (...elements) => { return elements; }; array(1,

2, 3); // [1, 2, 3]
22. ### const array = (...elements) => { return elements; }; array(1,

2, 3); // [1, 2, 3]
23. ### const array = (...elements) => { return elements; }; array(1,

2, 3); // [1, 2, 3]
24. ### const array = (...elements) => { return elements; }; array(1,

2, 3); // [1, 2, 3]
25. ### const log = (...args) => { console.log(...args); }; log('Hello', 'Codestock');

// Hello Codestock
26. ### const log = (...args) => { console.log(...args); }; log('Hello', 'Codestock');

// Hello Codestock
27. ### const langs = [ 'JavaScript', 'Elm', 'Haskell', ]; const [js,

...rest] = langs; js === 'JavaScript'; rest[0] === 'Elm'; rest[1] === 'Haskell';
28. ### const langs = [ 'JavaScript', 'Elm', 'Haskell', ]; const [js,

...rest] = langs; js === 'JavaScript'; rest[0] === 'Elm'; rest[1] === 'Haskell';
29. ### const langs = [ 'JavaScript', 'Elm', 'Haskell', ]; const [js,

...rest] = langs; js === 'JavaScript'; rest[0] === 'Elm'; rest[1] === 'Haskell';
30. ### const langs = [ 'JavaScript', 'Elm', 'Haskell', ]; const [js,

...rest] = langs; js === 'JavaScript'; rest[0] === 'Elm'; rest[1] === 'Haskell';

1;

1;

1;
34. ### const greet = (name, greeting = 'Hi') => { console.log(greeting,

name); }; greet('Codestock', 'Hello'); // Hello Codestock greet('Knoxville'); // Hi Knoxville
35. ### const greet = (name, greeting = 'Hi') => { console.log(greeting,

name); }; greet('Codestock', 'Hello'); // Hello Codestock greet('Knoxville'); // Hi Knoxville
36. ### const greet = (name, greeting = 'Hi') => { console.log(greeting,

name); }; greet('Codestock', 'Hello'); // Hello Codestock greet('Knoxville'); // Hi Knoxville
37. ### const greet = (name, greeting = 'Hi') => { console.log(greeting,

name); }; greet('Codestock', 'Hello'); // Hello Codestock greet('Knoxville'); // Hi Knoxville
38. ### Object.assign( {}, { hello: 'Knoxville' }, { hi: 'Codestock' }

); // { // hello: 'Knoxville', // hi: 'Codestock' // }
39. ### Object.assign( {}, { hello: 'Knoxville' }, { hi: 'Codestock' }

); // { // hello: 'Knoxville', // hi: 'Codestock' // }
40. ### Object.assign( {}, { hello: 'Knoxville' }, { hi: 'Codestock' }

); // { // hello: 'Knoxville', // hi: 'Codestock' // }
41. ### class Point { constructor(x, y) { this.x = x; this.y

= y; } moveBy(dx, dy) { this.x += dx; this.y += dy; } } function Point(x, y) { this.x = x; this.y = y; } Point.prototype.moveBy = function(dx, dy) { this.x += dx; this.y += dy; };

44. ### const add = (x, y) => x + y; add(2,

3) === 5; add(2, 3) === 5; add(2, 3) === 5; Referentially transparent
45. ### × let name = 'Jeremy'; const getName = () =>

name; const setName = (newName) => { name = newName; }; const printUpperName = () => { console.log(name.toUpperCase()); };
46. ### × let name = 'Jeremy'; const getName = () =>

name; const setName = (newName) => { name = newName; }; const printUpperName = () => { console.log(name.toUpperCase()); };
47. ### × let name = 'Jeremy'; const getName = () =>

name; const setName = (newName) => { name = newName; }; const printUpperName = () => { console.log(name.toUpperCase()); };
48. ### × let name = 'Jeremy'; const getName = () =>

name; const setName = (newName) => { name = newName; }; const printUpperName = () => { console.log(name.toUpperCase()); };
49. ### describe('api', () => { beforeEach(() => mockConsoleLog()); afterEach(() => restoreConsoleLog());

it('sets and prints the name', () => { printUpperName(); expect(console.log).calledWith('JEREMY'); setName('Jet'); printUpperName(); expect(console.log).calledWith('JET'); }); }); ×

51. ### const upperName = (name) => name.toUpperCase(); describe('api', () => {

it('returns an uppercase name', () => { expect(upperName('Jeremy')).to.equal('JEREMY'); expect(upperName('Jet')).to.equal('JET'); }); }); ✓

53. ### function doubleNumbers(numbers) { const doubled = []; const l =

numbers.length; for (let i = 0; i < l; i++) { doubled.push(numbers[i] * 2); } return doubled; } doubleNumbers([1, 2, 3]); // [2, 4, 6]

55. ### function doubleNumbers(numbers) { return numbers.map(n => n * 2); }

doubleNumbers([1, 2, 3]); // [2, 4, 6]
56. ### function doubleNumbers(numbers) { return numbers.map(n => n * 2); }

doubleNumbers([1, 2, 3]); // [2, 4, 6]
57. ### 1 2 3 numbers.map(n => n * 2) 2 4

6 Domain Range

59. ### const hobbies = [ 'programming', 'reading', 'music', ]; const firstTwo

= hobbies.splice(0, 2); console.log(firstTwo); // ['programming', 'reading'] console.log(hobbies); // ['music']
60. ### const hobbies = [ 'programming', 'reading', 'music', ]; const firstTwo

= hobbies.splice(0, 2); console.log(firstTwo); // ['programming', 'reading'] console.log(hobbies); // ['music'] Slight typo + mutability

62. ### const hobbies = Object.freeze([ 'programming', 'reading', 'music', ]); const firstTwo

= hobbies.splice(0, 2); // TypeError
63. ### const hobbies = Object.freeze([ 'programming', 'reading', 'music', ]); const firstTwo

= hobbies.splice(0, 2); // TypeError

65. ### class Point { constructor(x, y) { this.x = x; this.y

= y; } moveBy(dx, dy) { this.x += dx; this.y += dy; } } const point = new Point(0, 0); point.moveBy(5, 5); point.moveBy(-2, 2); console.log([point.x, point.y]); // [3, 7] ×
66. ### const createPoint = (x, y) => Object.freeze([x, y]); const movePointBy

= ([x, y], dx, dy) => { return Object.freeze([x + dx, y + dy]); }; let point = createPoint(0, 0); point = movePointBy(point, 5, 5); point = movePointBy(point, -2, 2); console.log(point); // [3, 7] ✓ (*or object-oriented without mutation)
67. ### const createPoint = (x, y) => Object.freeze([x, y]); const movePointBy

= ([x, y], dx, dy) => { return Object.freeze([x + dx, y + dy]); }; let point = createPoint(0, 0); point = movePointBy(point, 5, 5); point = movePointBy(point, -2, 2); console.log(point); // [3, 7] ✓ (*or object-oriented without mutation)
68. ### const createPoint = (x, y) => Object.freeze([x, y]); const movePointBy

= ([x, y], dx, dy) => { return Object.freeze([x + dx, y + dy]); }; let point = createPoint(0, 0); point = movePointBy(point, 5, 5); point = movePointBy(point, -2, 2); console.log(point); // [3, 7] ✓ (*or object-oriented without mutation)
69. ### const createPoint = (x, y) => Object.freeze([x, y]); const movePointBy

= ([x, y], dx, dy) => { return Object.freeze([x + dx, y + dy]); }; let point = createPoint(0, 0); point = movePointBy(point, 5, 5); point = movePointBy(point, -2, 2); console.log(point); // [3, 7] ✓ (*or object-oriented without mutation)
70. ### PROS SAFETY FROM ACCIDENTAL MUTATION FREE UNDO/REDO LOGS — REDUX

EXPLICIT FLOW OF DATA CONCURRENCY SAFETY
71. ### CONS VERBOSE MORE OBJECT CREATION* MORE GARBAGE COLLECTION* MORE MEMORY

USAGE* *Alleviated with libs like Immutable.js

73. ### const multiply = (x, y) => x * y; function

add(x, y) { return x + y; } const addAlias = add; const evens = [1, 2, 3].map(n => n * 2);
74. ### const multiply = (x, y) => x * y; function

add(x, y) { return x + y; } const addAlias = add; const evens = [1, 2, 3].map(n => n * 2);
75. ### const multiply = (x, y) => x * y; function

add(x, y) { return x + y; } const addAlias = add; const evens = [1, 2, 3].map(n => n * 2);
76. ### const multiply = (x, y) => x * y; function

add(x, y) { return x + y; } const addAlias = add; const evens = [1, 2, 3].map(n => n * 2);

80. ### const request = (options) => { return fetch(options.url, options) .then(resp

=> resp.json()); }; const usersPromise = request({ url: '/users', headers: { 'X-Custom': 'mykey' } }); const tasksPromise = request({ url: '/tasks', headers: { 'X-Custom': 'mykey' } });
81. ### const request = (options) => { return fetch(options.url, options) .then(resp

=> resp.json()); }; const usersPromise = request({ url: '/users', headers: { 'X-Custom': 'mykey' } }); const tasksPromise = request({ url: '/tasks', headers: { 'X-Custom': 'mykey' } }); Repetitive
82. ### const createRequester = (options) => { return (otherOptions) => {

return request(Object.assign( {}, options, otherOptions )); }; }; const customRequest = createRequester({ headers: { 'X-Custom': 'mykey' } }); const usersPromise = customRequest({ url: '/users' }); const tasksPromise = customRequest({ url: '/tasks' });
83. ### const createRequester = (options) => { return (otherOptions) => {

return request(Object.assign( {}, options, otherOptions )); }; }; const customRequest = createRequester({ headers: { 'X-Custom': 'mykey' } }); const usersPromise = customRequest({ url: '/users' }); const tasksPromise = customRequest({ url: '/tasks' });

86. ### const createAdder = (x) => { return (y) => x

+ y; }; const createRequester = (options) => { return (otherOptions) => { return request(Object.assign( {}, options, otherOptions )); }; }; RECALL

89. ### const request = (defaults, options) => { options = Object.assign({},

defaults, options); return fetch(options.url, options) .then(resp => resp.json()); }; const customRequest = partial(request, { headers: { 'X-Custom': 'mykey' } }); const usersPromise = customRequest({ url: '/users' }); const tasksPromise = customRequest({ url: '/tasks' });
90. ### const request = (defaults, options) => { options = Object.assign({},

defaults, options); return fetch(options.url, options) .then(resp => resp.json()); }; const customRequest = partial(request, { headers: { 'X-Custom': 'mykey' } }); const usersPromise = customRequest({ url: '/users' }); const tasksPromise = customRequest({ url: '/tasks' });
91. ### const partialFromBind = (fn, ...args) => { return fn.bind(null, ...args);

}; const partial = (fn, ...args) => { return (...otherArgs) => { return fn(...args, ...otherArgs) }; };
92. ### const partialFromBind = (fn, ...args) => { return fn.bind(null, ...args);

}; const partial = (fn, ...args) => { return (...otherArgs) => { return fn(...args, ...otherArgs) }; };
93. ### const partialFromBind = (fn, ...args) => { return fn.bind(null, ...args);

}; const partial = (fn, ...args) => { return (...otherArgs) => { return fn(...args, ...otherArgs) }; };
94. ### const partialFromBind = (fn, ...args) => { return fn.bind(null, ...args);

}; const partial = (fn, ...args) => { return (...otherArgs) => { return fn(...args, ...otherArgs) }; };
95. ### const partialFromBind = (fn, ...args) => { return fn.bind(null, ...args);

}; const partial = (fn, ...args) => { return (...otherArgs) => { return fn(...args, ...otherArgs) }; };
96. ### const partialFromBind = (fn, ...args) => { return fn.bind(null, ...args);

}; const partial = (fn, ...args) => { return (...otherArgs) => { return fn(...args, ...otherArgs) }; };
97. ### CURRYING

request({ headers: { 'X-Custom': 'mykey' } }); const usersPromise = customRequest({ url: '/users' }); const tasksPromise = customRequest({ url: '/tasks' });

request({ headers: { 'X-Custom': 'mykey' } }); const usersPromise = customRequest({ url: '/users' }); const tasksPromise = customRequest({ url: '/tasks' });
100. ### const add = x => y => x + y;

function add(x) { return function(y) { return x + y; }; }
101. ### const add = x => y => x + y;

function add(x) { return function(y) { return x + y; }; }
102. ### const add = x => y => x + y;

function add(x) { return function(y) { return x + y; }; }
103. ### const request = defaults => options => { options =

Object.assign( {}, defaults, options ); return fetch(options.url, options) .then(resp => resp.json()); };
104. ### const request = defaults => options => { options =

Object.assign( {}, defaults, options ); return fetch(options.url, options) .then(resp => resp.json()); };

106. ### const map = fn => array => array.map(fn); const multiply

= x => y => x * y; const pluck = key => object => object[key]; const discount = multiply(0.98); const tax = multiply(1.0925); const customRequest = request({ headers: { 'X-Custom': 'mykey' } }); customRequest({ url: '/cart/items' }) .then(map(pluck('price'))) .then(map(discount)) .then(map(tax));
107. ### const map = fn => array => array.map(fn); const multiply

= x => y => x * y; const pluck = key => object => object[key]; const discount = multiply(0.98); const tax = multiply(1.0925); const customRequest = request({ headers: { 'X-Custom': 'mykey' } }); customRequest({ url: '/cart/items' }) .then(map(pluck('price'))) .then(map(discount)) .then(map(tax));
108. ### const map = fn => array => array.map(fn); const multiply

= x => y => x * y; const pluck = key => object => object[key]; const discount = multiply(0.98); const tax = multiply(1.0925); const customRequest = request({ headers: { 'X-Custom': 'mykey' } }); customRequest({ url: '/cart/items' }) .then(map(pluck('price'))) .then(map(discount)) .then(map(tax));
109. ### const map = fn => array => array.map(fn); const multiply

= x => y => x * y; const pluck = key => object => object[key]; const discount = multiply(0.98); const tax = multiply(1.0925); const customRequest = request({ headers: { 'X-Custom': 'mykey' } }); customRequest({ url: '/cart/items' }) .then(map(pluck('price'))) .then(map(discount)) .then(map(tax));
110. ### const map = fn => array => array.map(fn); const multiply

= x => y => x * y; const pluck = key => object => object[key]; const discount = multiply(0.98); const tax = multiply(1.0925); const customRequest = request({ headers: { 'X-Custom': 'mykey' } }); customRequest({ url: '/cart/items' }) .then(map(pluck('price'))) .then(map(discount)) .then(map(tax));
111. ### const map = fn => array => array.map(fn); const multiply

= x => y => x * y; const pluck = key => object => object[key]; const discount = multiply(0.98); const tax = multiply(1.0925); const customRequest = request({ headers: { 'X-Custom': 'mykey' } }); customRequest({ url: '/cart/items' }) .then(map(pluck('price'))) .then(map(discount)) .then(map(tax));
112. ### const map = fn => array => array.map(fn); const multiply

= x => y => x * y; const pluck = key => object => object[key]; const discount = multiply(0.98); const tax = multiply(1.0925); const customRequest = request({ headers: { 'X-Custom': 'mykey' } }); customRequest({ url: '/cart/items' }) .then(map(pluck('price'))) .then(map(discount)) .then(map(tax));
113. ### customRequest({ url: '/cart/items' }) .then(map(pluck('price'))) .then(map(discount)) .then(map(tax)); [ { price:

5 }, { price: 10 }, { price: 3 }, ]
114. ### [ { price: 5 }, { price: 10 }, {

price: 3 }, ] map(pluck('price')) [ 5, 10, 3, ] item.price
115. ### [ 5, 10, 3, ] map(discount) [ 4.9, 9.8, 2.94,

] price * 0.98
116. ### [ 5.35, 10.71, 3.21, ] map(tax) [ 4.9, 9.8, 2.94,

] price * 1.0925

118. ### const processWord = compose(hyphenate, reverse, toUpperCase); const words = [

'hello', 'functional', 'programming' ]; const newWords = words.map(processWord); console.log(newWords); // ['OL-LEH, 'LANOI-TCNUF', 'GNIMM-ARGORP']
119. ### const processWord = compose(hyphenate, reverse, toUpperCase); const words = [

'hello', 'functional', 'programming' ]; const newWords = words.map(processWord); console.log(newWords); // ['OL-LEH, 'LANOI-TCNUF', 'GNIMM-ARGORP']
120. ### const processWord = compose(hyphenate, reverse, toUpperCase); const processWordExplicit = (word)

=> { return hyphenate(reverse(toUpperCase(word))); };
121. ### const compose = (...fns) => arg => ( fns.reduceRight( (result,

fn) => fn(result), arg ) );
122. ### const compose = (...fns) => arg => ( fns.reduceRight( (result,

fn) => fn(result), arg ) );
123. ### const compose = (...fns) => arg => ( fns.reduceRight( (result,

fn) => fn(result), arg ) );
124. ### const compose = (...fns) => arg => ( fns.reduceRight( (result,

fn) => fn(result), arg ) );
125. ### const compose = (...fns) => arg => ( fns.reduceRight( (result,

fn) => fn(result), arg ) );
126. ### const compose = (...fns) => arg => ( fns.reduceRight( (result,

fn) => fn(result), arg ) );
127. ### const compose = (...fns) => arg => ( fns.reduceRight( (result,

fn) => fn(result), arg ) ); fns = [hyphenate, reverse, toUpperCase] arg = result = 'hello'
128. ### const compose = (...fns) => arg => ( fns.reduceRight( (result,

fn) => fn(result), arg ) ); fns = [hyphenate, reverse] result = 'HELLO'
129. ### const compose = (...fns) => arg => ( fns.reduceRight( (result,

fn) => fn(result), arg ) ); fns = [hyphenate] result = 'OLLEH'
130. ### const compose = (...fns) => arg => ( fns.reduceRight( (result,

fn) => fn(result), arg ) ); fns = [] result = 'OL-LEH'

EXAMPLE
132. ### customRequest({ url: '/cart/items' }) .then(map(pluck('price'))) .then(map(discount)) .then(map(tax)); Triple iteration RETURNING

TO PRICES EXAMPLE
133. ### customRequest({ url: '/cart/items' }) .then(map( compose( tax, discount, pluck('price') )

)); Single iteration

136. ### const factorial = (n) => { let result = 1;

while (n > 1) { result *= n; n--; } return result; };
137. ### const factorial = (n) => { if (n < 2)

{ return 1; } return n * factorial(n - 1); };
138. ### const factorial = (n) => { if (n < 2)

{ return 1; } return n * factorial(n - 1); }; Recursive call
139. ### const factorial = (n) => { if (n < 2)

{ return 1; } return n * factorial(n - 1); }; Base case

143. ### factorial(4); 4 * factorial(3); 4 * 3 * factorial(2); 4

* 3 * 2 * factorial(1);
144. ### factorial(4); 4 * factorial(3); 4 * 3 * factorial(2); 4

* 3 * 2 * factorial(1); 4 * 3 * 2 * 1;
145. ### factorial(4); 4 * factorial(3); 4 * 3 * factorial(2); 4

* 3 * 2 * factorial(1); 4 * 3 * 2 * 1; 4 * 3 * 2;
146. ### factorial(4); 4 * factorial(3); 4 * 3 * factorial(2); 4

* 3 * 2 * factorial(1); 4 * 3 * 2 * 1; 4 * 3 * 2; 4 * 6;
147. ### factorial(4); 4 * factorial(3); 4 * 3 * factorial(2); 4

* 3 * 2 * factorial(1); 4 * 3 * 2 * 1; 4 * 3 * 2; 4 * 6; 24;
148. ### STEPS Find the recurrence  (n × n-1 × n-2 ×

… 1)  Find the base case  (n < 2)

RESULT?
151. ### const value = factorial(100000); console.log(value); // ??? WHAT IS THE

RESULT? RangeError: Maximum call stack size exceeded
152. ### factorial(4); 4 * factorial(3); 4 * 3 * factorial(2); 4

* 3 * 2 * factorial(1); 4 * 3 * 2 * 1; 4 * 3 * 2; 4 * 6; 24; <main> Call Stack
153. ### factorial(4); 4 * factorial(3); 4 * 3 * factorial(2); 4

* 3 * 2 * factorial(1); 4 * 3 * 2 * 1; 4 * 3 * 2; 4 * 6; 24; <main> factorial(4) Call Stack
154. ### factorial(4); 4 * factorial(3); 4 * 3 * factorial(2); 4

* 3 * 2 * factorial(1); 4 * 3 * 2 * 1; 4 * 3 * 2; 4 * 6; 24; <main> factorial(4) factorial(3) Call Stack
155. ### factorial(4); 4 * factorial(3); 4 * 3 * factorial(2); 4

* 3 * 2 * factorial(1); 4 * 3 * 2 * 1; 4 * 3 * 2; 4 * 6; 24; <main> factorial(4) factorial(3) factorial(2) Call Stack
156. ### factorial(4); 4 * factorial(3); 4 * 3 * factorial(2); 4

* 3 * 2 * factorial(1); 4 * 3 * 2 * 1; 4 * 3 * 2; 4 * 6; 24; <main> factorial(4) factorial(3) factorial(2) factorial(1) Call Stack
157. ### factorial(4); 4 * factorial(3); 4 * 3 * factorial(2); 4

* 3 * 2 * factorial(1); 4 * 3 * 2 * 1; 4 * 3 * 2; 4 * 6; 24; <main> factorial(4) factorial(3) factorial(2) Call Stack
158. ### factorial(4); 4 * factorial(3); 4 * 3 * factorial(2); 4

* 3 * 2 * factorial(1); 4 * 3 * 2 * 1; 4 * 3 * 2; 4 * 6; 24; <main> factorial(4) factorial(3) Call Stack
159. ### factorial(4); 4 * factorial(3); 4 * 3 * factorial(2); 4

* 3 * 2 * factorial(1); 4 * 3 * 2 * 1; 4 * 3 * 2; 4 * 6; 24; <main> factorial(4) Call Stack
160. ### factorial(4); 4 * factorial(3); 4 * 3 * factorial(2); 4

* 3 * 2 * factorial(1); 4 * 3 * 2 * 1; 4 * 3 * 2; 4 * 6; 24; <main> Call Stack
161. ### const value = factorial(100000); console.log(value); // ??? 100,000 calls =

100,000 stack frames 1 stack frame ≈ 48B Max stack usage ≈ 1MB 100,000 x 48 / 1024 / 1024 = 4.58MB > 1MB
162. ### IN ES2015!* *SAFARI, CHROME WITH FLAG, NODE WITH FLAG TAIL

CALL OPTIMIZATION

164. ### const factorial = (n) => { if (n < 2)

{ return 1; } return n * factorial(n - 1); }; UNOPTIMIZABLE
165. ### const factorial = (n) => { if (n < 2)

{ return 1; } return n * factorial(n - 1); }; 1 UNOPTIMIZABLE
166. ### const factorial = (n) => { if (n < 2)

{ return 1; } return n * factorial(n - 1); }; 1 UNOPTIMIZABLE 2
167. ### const factorial = (n) => { if (n < 2)

{ return 1; } return n * factorial(n - 1); }; 1 UNOPTIMIZABLE 2 3
168. ### const factorial = (n, accum = 1) => { if

(n < 2) { return accum; } return factorial(n - 1, n * accum); }; OPTIMIZABLE
169. ### const factorial = (n, accum = 1) => { if

(n < 2) { return accum; } return factorial(n - 1, n * accum); }; OPTIMIZABLE
170. ### const factorial = (n, accum = 1) => { if

(n < 2) { return accum; } return factorial(n - 1, n * accum); }; OPTIMIZABLE
171. ### const factorial = (n, accum = 1) => { if

(n < 2) { return accum; } return factorial(n - 1, n * accum); }; OPTIMIZABLE
172. ### const factorial = (n, accum = 1) => { if

(n < 2) { return accum; } return factorial(n - 1, n * accum); }; 1 OPTIMIZABLE
173. ### const factorial = (n, accum = 1) => { if

(n < 2) { return accum; } return factorial(n - 1, n * accum); }; 1 2 OPTIMIZABLE
174. ### const factorial = (n, accum = 1) => { if

(n < 2) { return accum; } return factorial(n - 1, n * accum); }; 1 2 3 OPTIMIZABLE

176. ### factorial(4 /*, 1 */); factorial(3, 4); factorial(2, 12); factorial(1, 24);

24; <main> Call Stack
177. ### factorial(4 /*, 1 */); factorial(3, 4); factorial(2, 12); factorial(1, 24);

24; <main> factorial(4, 1) Call Stack
178. ### factorial(4 /*, 1 */); factorial(3, 4); factorial(2, 12); factorial(1, 24);

24; <main> factorial(3, 4) Call Stack
179. ### factorial(4 /*, 1 */); factorial(3, 4); factorial(2, 12); factorial(1, 24);

24; <main> factorial(2, 12) Call Stack
180. ### factorial(4 /*, 1 */); factorial(3, 4); factorial(2, 12); factorial(1, 24);

24; <main> factorial(1, 24) Call Stack
181. ### factorial(4 /*, 1 */); factorial(3, 4); factorial(2, 12); factorial(1, 24);

24; <main> Call Stack