/** * Functional Programming!!! */ const val = 5, /** * The identity function takes a function and returns a copy of that function. * The use of the spread (...) operator here is key to understanding more * complex functional programming, like curry() below. */ identity = f => (...args) => f(...args), /** * Here are three of the key functions to understand for functional programming. * * curry() takes a non-unary function that accepts multiple functors and returns * a function that accepts exactly one functor * * pipe() accepts a set of transform functions and returns a function that applies * each of these transforms, one at a time, to the given functor * * compose() works just like pipe(), just in reverse. For example, given the composed * function f(g(h(x))), you can rewrite it as a pipe: * * pipe(h, g, f) * * or as a composition: * * compose(f, g, h) * * Notice that compose follows the same order as if you were writing out the complete * chain, while pipe requires the transforms to be in the order you would like to * apply them, much like gates filter material flowing through a pipe. */ curry = (f, arr = []) => (...args) => (a => (a.length === f.length ? f(...a) : curry(f, a)))([...arr, ...args]), pipe = (...funcs) => x => funcs.reduce((v, f) => f(v), x), compose = (...funcs) => x => funcs.reduceRight((v, f) => f(v), x), /** * Here are some basic unary transformation functions - they take * a single functor (input) and transform & return it */ double = x => x * 2, square = x => x * x, half = x => x / 2, /** * This transformation function is configurable via a parameter, * so instead of just passing it directly you'll actually want to call * it with a value and pass the function it returns - now you're thinking * functionally! */ add = x => y => x + y, /** * Here's a basic non-unary function - it doesn't fit too well in a pipe/ * compose function, so we'll curry it to split it up into a chain of unary * functions! * * curryedSum can be called in a few different ways: * * curryedSum(1, 2, 3) // 6 * curryedSum(1, 2)(3) // 6 * curryedSum(1)(2, 3) // 6 * curryedSum(1)(2)(3) // 6 * * You can also partially compose it, as such: * * let foo = curryedSum(1, 2) // x => 1 + 2 + x * let bar = curryedSum(1) // x => 1 + x + y * and then use it in a pipe: * * pipe(foo) * pipe(bar(2)) */ sum = (x, y, z) => x + y + z, curryedSum = curry(sum), /** * Here's a basic unary trace function - it simply logs the current value * it is passed with a tag. */ trace = tag => x => { console.log(` [${tag}] => ${x}`) return x }, /** * The following transform functions will all have the same result! */ transform = x => double(square(add(2)(x))), composedTransform = compose(double, square, add(2)), pipedTransform = pipe(add(2), square, double), pipedTracedTransform = pipe( trace('initial'), add(2), trace('add(2)'), square, trace('square'), double, trace('double') ) console.log() console.log(`Transform takes a number (${val}) and performs the following`) console.log('operations:') console.log(' - adds 2') console.log(' - squares the value') console.log(' - doubles the value') console.log() console.log(' transform: ' + transform(val)) console.log('composedTransform: ' + composedTransform(val)) console.log(' pipedTransform: ' + pipedTransform(val)) console.log() console.log('By simply adding a trace() call to the pipe, we can watch the') console.log('transformation at each step, like so:') console.log() console.log('\n returned value: ' + pipedTracedTransform(val)) console.log()