Skip to content

Instantly share code, notes, and snippets.

@superhawk610
Last active February 1, 2018 05:16
Show Gist options
  • Save superhawk610/dcd5bbca786e05f6533896267bc9f058 to your computer and use it in GitHub Desktop.
Save superhawk610/dcd5bbca786e05f6533896267bc9f058 to your computer and use it in GitHub Desktop.
Wrap your head around functional programming!
/**
* 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()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment