Skip to content

Instantly share code, notes, and snippets.

@AndrewThian
Created August 15, 2018 06:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save AndrewThian/0bb3268c7e353391d2c98d5871207bda to your computer and use it in GitHub Desktop.
Save AndrewThian/0bb3268c7e353391d2c98d5871207bda to your computer and use it in GitHub Desktop.
A simple gist to explain using HoF
// Simple logging functionality
const sqrt = Math.sqrt;
console.log("result", sqrt(16))
console.log("result", sqrt(9))
// ======= logging ====== //
// Introducing logging within the function
// we want to log the incoming parameters in the function
const sqrt = int => {
console.log(int)
return Math.sqrt(int)
}
console.log("result", sqrt(16))
console.log("result", sqrt(9))
// ======= higher order function logging ====== //
// We can do better than log within the sqrt function
// Higher order logger to allow us to pass in
// any function and it will log the parameters
const log = fn => (...args) => {
// takes in a function as a parameter
// returns a function with arguments
console.log(...args)
return fn(...args)
}
// we don't have to pollute the Math.sqrt function
// log() wraps the Math.sqrt fn and returns a new fn with logging capabilities
// that we can assign to function variable to use
const sqrt = log(Math.sqrt)
console.log("result", sqrt(16))
console.log("result", sqrt(9))
// ======= higher order caching ====== //
// lets take it a step further to include caching into our logging HoF
const log = fn => (...args) => {
// takes in a function as a parameter
// returns a function with arguments
console.log(...args)
return fn(...args)
}
// we have a memoize fn that takes in a fn and returns another fn
// where we initialize a little store..
const memoize = fn => {
const results = {}
return (...args) => {
const key = JSON.stringify(args)
// if our results store already has a key
// we should return that key's value
// otherwise, set the key's value to the executed function
return results.hasOwnProperty(key) ? results[key] : (results[key] = fn(...args))
}
}
// now our sqrt function has 2 additional capabilities..
// caching and logging functionality.
const sqrt = memoize(log(Math.sqrt))
console.log("result", sqrt(16))
// 16
// result 4
console.log("result", sqrt(9))
// 9
// result 3
// now if we run sqrt(16) again
console.log("result", sqrt(16))
// result 4
// we do not log the function execution again
// because our memoize decorator already cached
// the value of sqrt(16)
// now we have two higher order function(decorators)
// ======= composing functions ====== //
// using nested decorators is a bit messy
// we can create an abstraction to avoid nesting
// compose -> well known abstraction
const compose = (...fns) => arg => {
// it takes an array of functions
// returns a function that takes in an argument
// we use reduceRight array method:
// MDN -> https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/ReduceRight
// takes two parameters (result, fn)
// passing the argument recursively into each accumulator function
return fns.reduceRight((result, fn) => fn(result), arg)
}
// seems more ergonomic
// however, this doesn't work if we have multiple parameters in our child function
const sqrt = compose(memoize, log)(Math.sqrt)
// ===== multiple arguments ===== //
// I know this seems a bit crazy
const compose = (...fns) => (...args) => {
return fns.reduceRight((result, fn) => [fn(...result)], args)[0]
}
// lemme break it down
const compose = (...fns) => (...args) => {
// reduceRight accumulates an array from the right
// i.e accumulate from the last index in the array
// rather than the first
const _fns = fns.reduceRight((result, fn) => {
// result is the accumulator
// result here is equivalent to args on line 122
console.log("result: ", ...result)
// fn here is the function variable log || memoize
console.log("fn: ", fn)
console.log("pass in arguments via spread operator into fn")
// passing the accumulator into each composing function
// will return a function that gets added to the next iteration
let func = fn(...result)
console.log("func: ", func)
return [func]
}, args)
console.log("compose: ", _fns)
// finally we return the composed function to the variable
return _fns[0]
}
// ===== Reference ===== //
// typescript and decorators: https://www.youtube.com/watch?v=lepKIVH3ZfE
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment