Skip to content

Latest commit

 

History

History
371 lines (287 loc) · 10 KB

day_12.md

File metadata and controls

371 lines (287 loc) · 10 KB

DAY 12

  • Destructuring
    • Syntax
    • Examples
    • Readings
  • Advanced Function/code factorization
    • Currying
    • Partial application
    • First-class composition
    • Readings

Destructuring

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:

Advanced Function/code factorization

Currying

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"

Partial Application

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

First-class composition

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 function h(t), and the oxygen concentration at elevation x is given by the function c(x), then (c ∘ h)(t) describes the oxygen concentration around the plane at time t. 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

Readings: