Sometimes, the elegant implementation is just a function. Not a method. Not a class. Not a framework. Just a function.

- John Fucking Carmack


// A Function

const add = (x, y) => x + y

add(1, 2) // 3

// Types



// add :: (Number, Number) -> Number
const add = (x, y) => x + y
// concat :: ([a], [a]) -> [a]
const concat = (xs, ys) => [...xs, ...ys]

concat([1, 2], [3, 4]) // [1, 2, 3, 4]
// reduce :: ((b, a) -> b), b, [a]) -> b 
const reduce = (f, acc, [x, ...xs]) => 
    x === undefined ?  acc : reduce(f, f(acc, x), xs)
reduce(concat, [], [[1, 2], [3, 4]]) // [1, 2, 3, 4]
reduce(add, 0, [1, 2, 3]) // 6
// reducer :: (b, a) -> b
// using reduce
// sum :: [Number] -> Number
const sum = numbers => reduce(add, 0, numbers)

// flatten :: [[a]] -> [a]
const flatten = xs => reduce(concat, [], xs)
const reducer = (obj, [k, v]) => ({...obj, [k]: v})

// fromPairs :: [[String, a]] -> {String: a}
const fromPairs = pairs => reduce(reducer, {}, pairs)

fromPairs([['one', 1], ['two', 2]]) // {one:1, two: 2}
// using reduce

// reducer :: (b, a) -> b
const reducer = (acc, x) => [...acc, f(x)]
// reducer :: (b, a) -> b
const reducer = (acc, e) => f(e) ? [...acc, e] : acc
// map :: ((a -> b), [a]) -> [b]
const map = (f, xs) => 
    reduce(reducer, [], xs)
// filter :: ((a -> Boolean), [a]) -> [a]
const filter = (f, xs) => 
    reduce(reducer, [], xs)
// using map
// props :: ([String], {String: a}) -> [a]
const props = (keys, object) => map(k => prop(k, object), keys)
// pluck :: (String, [{String: a}]) -> [a]
const pluck = (key, xs) => map(x => prop(key, x), xs)
// Compose
// compose2 = (b -> c, a -> b) -> (a -> c)
compose2 = (f, g) => x => f(g(x)
// flatten :: [[a]] -> [a]
// sum :: [Number] -> Number
// flatSum :: [[Number]] -> Number
const flatSum = compose2(sum, flatten)

flatSum([[1, 2], [3, 4]]) // 10
// Compose
// compose = (y -> z, ...,  a -> b) -> (a -> z)
const compose = (...fs) => x => reduce(compose2, x => x, fs)
// flatSumTotalBiggerThan100 :: [[Number]] -> Boolean
const flatSumBiggerThan100 = compose(x => x > 100, sum, flatten)
// Limitation

// only works when functions take 1 argument

compose(map, sum) // :(
// Currying
// prop :: String -> {String: a} -> a
const prop = key => obj => obj[key]
// prop :: (String, {String: a}) -> a
const prop (key, obj) => obj[key]
// Currying

// sumNumbers :: {numbers: [Number]} -> Number
const sumNumbers = compose(sum, prop('numbers'))

sumNumbers({numbers: [1, 2, 3]}) // 6
// Currying


const id = prop('id')(obj)

const curry = fn => {
    const curried  = (f, init) => 
        (...args) => {
          const acc = [...init, ...args]
          return acc.length >= f.length ? f(...acc) : curried(f, acc)
        }

    return curried(fn, [])

}
// Currying


const prop = curry((key, obj) => obj[key])


prop('id', obj) === prop('id')(obj)

// Currying


// map :: (a -> b) -> [a] -> [b]
const map = curry((f, xs) => 
    reduce((acc, x) => [...acc, f(x)], [], xs))
// getTotalPrice :: [{price: Number}] -> Number
const getTotalPrice = compose(sum, map(prop('price')))
// filter = (a -> Boolean) -> [a] -> [a]
const filter = 
    curry((f, xs) => reduce((acc, x) => f(x) ? [...acc, x] : acc, [], xs))

// getTotalPrice :: [{price: Number}] -> Number
const getTotalPrice = 
    compose(sum, map(prop('price')), filter(prop('inStock')))
/*
 * A function is a relation between 1 input and 1 output
 */
/* 
 * Functions are stateless
 *
 * Given the same input 
 * they will always return the same output
 */
/* 
 * The only thing a function does is 
 * compute an output value
 */
/* 
 * Functions are composable like legos
 */
/*
 * Lego is awesome!
 */
// The secret sauce... Lining up types


// getTotal :: [{price: Number}] -> Number
const getTotal = compose(sum, map(prop('price')))
// parseQuery :: String -> {String: String}
const parseQuery = compose(fromPairs, map(split('=')), split('&'))
// questions ?

Copy of Keep calm and curry on

By James Chow

Copy of Keep calm and curry on

  • 98