- Destructuring
- Syntax
- Examples
- Readings
- Advanced Function/code factorization
- Currying
- Partial application
- First-class composition
- Readings
The destructuring assignment syntax is a JavaScript expression that makes it possible to unpack values from arrays, or properties from objects, into distinct variables.
Source Wikipedia
Ok nice, but, what's that?
A better explanation is defined later on that document
The object and array literal expressions provide an easy way to create ad hoc packages of data.
var x = [1, 2, 3, 4, 5];
The destructuring assignment uses similar syntax, but on the left-hand side
of the assignment to define what values to unpack from the sourced variable.
let x = [1, 2, 3, 4, 5];
let [y, z] = x;
console.log(y); // 1
console.log(z); // 2
Source Wikipedia
Now that's better, if we think in terms of unpacking it's much easier to understand.
But this is only the beginning, this new feature enables a lot of new patterns which were hard to write and maintain before ES6 or event impossible to write.
Object destructuring:
let x = {a: 1, b:2};
let {a, b} = x;
console.log(a); // 1
console.log(b); // 2
Variable Swapping
let a = 1;
let b = 3;
[a, b] = [b, a];
console.log(a); // 3
console.log(b); // 1
Default values
let {a = 0, b = null} = someObject;
Even destructuring function parameters + adding default values
/**
*
* @param {object} $0
* @param {string} $0.a
* @param {number} $0.b
* @returns {String}
*/
function f ({ a = 'default string', b = 0 } = {}) {
return `${a}, ${b}`;
}
And leveraging delayed evaluation you can make a destructured parameter to be mandatory
/**
*
* @param {object} $0
* @param {string} $0.a
* @param {number} $0.b
* @returns {String}
*/
function f ({
a = (function(){ throw new Error('a is mandatory')}()),
b = 0
} = {}) {
return `${a}, ${b}`;
}
f(); // will throw
f({a:'hi'}); // will return "hi, 0"
Let's take some time an go through the following documents:
- MDN: Destructuring assignment
- YDKJS: ES6 & Beyond : Syntax -> Destructuring
- 2ality: Destructuring and parameter handling in ECMAScript 6
- David Walsh: Destructuring and Function Arguments
In mathematics and computer science, currying is the technique of translating the evaluation of a function that takes multiple arguments into evaluating a sequence of functions, each with a single argument. For example, a function that takes two arguments, one from X and one from Y, and produces outputs in Z, by currying is translated into a function that takes a single argument from X and produces as outputs functions from Y to Z. Currying is related to, but not the same as, partial application. Wikipedia
/**
* No curry for you
*
* @param {number} x
* @param {number} y
* @returns {number}
*/
function add(x, y){
return x + y;
}
add(3, 4); // > 7
/**
* Add some spicy curry
*
* @param {number} x
* @returns {function}
*/
function add_curry(x) { // :P
return function(y) {
return x + y;
}
}
add_curry(3)(4); // 7
const message = 'Today, [date], [user] says [salutation]';
const message2 = 'Hellow [user] you have to say [salutation] before the end of [date]';
const date = new Date();
const user = { name: 'Javier', surname: 'Valderrama', nickName: 'jax' };
const salutation = 'hi';
/**
* Not Curried
*
* @param {String} message
* @param {Object} date
* @param {Object} user
* @param {String} salutation
* @returns {String}
*/
function formatMessage (message, date, user, salutation) {
return message.replace('[date]', date.toLocaleDateString())
.replace('[user]', user.nickName.toUpperCase())
.replace('[salutation]', salutation.replace(/hi/i, 'howdy'))
}
formatMessage(
message,
date,
user,
salutation
) // > "Today, 24/6/2019, JAX says howdy"
/**
* Curried
*
* @param {String} salutation
* @param {Object} user
* @param {Object} date
* @param {String} message
*
* @returns {Function|String}
*/
let formatMessageCurried = (salutation) => (user) => (date) => (message) => {
return message.replace('[date]', date.toLocaleDateString())
.replace('[user]', user.nickName.toUpperCase())
.replace('[salutation]', salutation.replace(/hi/i, 'howdy'))
}
/**
* Not very interesting
*/
formatMessageCurried(salutation)(user)(date)(message) // > "Today, 24/6/2019, JAX says howdy"
/**
* This is really interesting
*/
const formatMsgWithSalutationUserAndDate = formatMessageCurried(salutation)(user)(date);
formatMsgWithSalutationUserAndDate(message) // > "Today, 24/6/2019, JAX says howdy"
formatMsgWithSalutationUserAndDate(message2) // > "Hellow JAX you have to say howdy before the end of 24/6/2019"
In computer science, partial application (or partial function application) refers to the process of fixing a number of arguments to a function, producing another function of smaller arity. Wikipedia
Difference from Currying
One of the significant differences between the two is that a call to a partially applied function returns the result right away, not another function down the currying chain. Wikipedia
/**
* No partial
*
* @param {number} x
* @param {number} y
* @returns {number}
*/
function add(x, y){
return x + y;
}
add(3, 4); // > 7
/**
* Partial
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind#Partially_applied_functions
*
* @param {number} y
* @returns {number}
*/
var add_partial = add.bind(null, 3);
add_partial(4); // -> 7
/**
* Using multilevel binary partially applied functions to retunr an unary
* (f ∘ g )(v)
*
* @param {...function} args
* @returns {Function}
*/
function multiLevelPartialRight (...args) {
let cfn = (v) => v;
while (args.length) {
cfn = args.pop().bind(null, cfn)
}
return cfn
}
multiLevelPartialRight(
/* f */(fn, v) => fn(v) + 1,
/* g */(fn, v) => fn(v) * 2
)(2) // > (f ∘ g )(v) = 5
In computer science, function composition is an act or mechanism to combine simple functions to build more complicated ones. Like the usual composition of functions in mathematics, the result of each function is passed as the argument of the next, and the result of the last one is the result of the whole. Wikipedia
Simple use-case
If an airplane's elevation at time
t
is given by the functionh(t)
, and the oxygen concentration at elevationx
is given by the functionc(x)
, then(c ∘ h)(t)
describes the oxygen concentration around the plane at timet
. Wikipedia
A well known usecase is a mapper or transducer
From a given response data, remove duplicates, normalize numbers, sort by N.... and so on
Notes:
- the flow is right-to-left, calling each function with the output of the latest provided first.
- JavaScript doesn't have a
composition
operator like Haskell - JavaScript have Lambda expressions!!
/**
* simplest imperative nested solution
* n(x) = f(g(x))
*
* Notation:
* ( f ∘ g )(x) = f(g(x) // f ∘ g means f after g
*
* Alternative postfix notation:
* (xg)f
* @see https://en.wikipedia.org/wiki/Reverse_Polish_notation
*
* @param {Function} f
* @param {Function} g
*/
function fog (f, g) { return (x) => f(g(x)) };
fog(
/* f */ v => v + 1,
/* g */ v => v * 2
)(2) // > (f ∘ g )(v) = 5
/**
* Iterative solution
* (f ∘ g )(v)
*
* @param {...function} args
* @returns {Function}
*/
function composeSequenceRight (...args) {
return (v) => {
while (args.length) {
v = args.pop()(v)
}
return v;
}
}
composeSequenceRight(
/* f */ v => v + 1,
/* g */ v => v * 2
)(2) // > (f ∘ g )(v) = 5
/**
* Iterative oneliner functional style solution
* (f ∘ g )(v)
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/ReduceRight
*
* @param {...function} args
* @returns {Function}
*/
const composeRight = (...args) => (value) => args.reduceRight((acc, fn) => fn(acc), value)
composeRight(
/* f */ v => v + 1,
/* g */ v => v * 2
)(2) // > (f ∘ g )(v) = 5