-
-
Save iamstarkov/b1d0d8d7231be4f956da5af82620b096 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// some fp boilerplate | |
const map = fn => xs => xs.map(fn); | |
const reduce = (fn, init) => xs => xs.reduce(fn, init); | |
const concat = (a, b) => a.concat(b); | |
/* or in a library */ | |
const R = require('ramda'); | |
// async helpers | |
const toPromise = x => Promise.resolve(x); | |
const promiseAll = xs => Promise.all(xs); | |
// decent fp | |
const getUsersData = regionCode => toPromise(regionCode) | |
.then(getUsersIdList) | |
.then(map(fetchUserData)) | |
.then(promiseAll); | |
// fp, point-free way | |
const getUsersData = R.pipeP(toPromise, | |
getUsersIdList, | |
R.map(fetchUserData), | |
promiseAll | |
); | |
// decent fp | |
const getPostsData = regionCode => toPromise(regionCode) | |
.then(getUsersData) | |
.then(map(getPostsIds)) | |
.then(reduce(concat, [])) | |
.then(map(fetchPostData)) | |
.then(promiseAll); | |
// fp, point-free way | |
const getPostsData = R.pipeP(toPromise, | |
getUsersData, | |
R.map(getPostsIds), | |
R.reduce(R.concat, []), | |
R.map(fetchPostData), | |
promiseAll | |
) | |
/** | |
* Some science behind this refactoring: | |
* https://github.com/iamstarkov/fp-js-workshop | |
*/ | |
/** | |
* all functions in ramda are curried, but its easy to implement "curry" on your own | |
* See https://iamstarkov.com/fp-js-workshop/02-practical-intro/#/7 | |
* 4 lines wo/ comments: | |
const curry = fn => (...args) => | |
args.length < fn.length | |
? (...rest) => curry(fn)(...args, ...rest) | |
: fn(...args) | |
*/ | |
const curry = fn => // takes function | |
// returns a function to await for all arguments | |
(...args) => | |
// if not all arguments provided | |
args.length < fn.length | |
// return curried function which accumulates other required arguments | |
? (...rest) => curry(fn)(...args, ...rest) | |
// if all arguments are provided, | |
// just invoke function with them | |
: fn(...args) | |
/** | |
* then there is a concept of compose — right-to-left composition: f(g(x)) = compose(f, g)(x), | |
* its nice from Math POV, but not really human friendly, | |
* because you would like to compose left to right — same way you read: compose(a, b)(x) = b(a(x)) | |
* See https://iamstarkov.com/fp-js-workshop/02-practical-intro/#/7 | |
* 3 lines wo/ comments: | |
const pipe = (headFN, ...restFns) => (...args) => restFns.reduce( | |
(value, fn) => fn(value), | |
headFN(...args), | |
); | |
*/ | |
// takes functions | |
// separate left most function from rest functions | |
const pipe = (headFN, ...restFns) => | |
// return piped function, | |
(...args) => // which takes any arguments | |
// invoke rest functions after each other | |
restFns.reduce( | |
// each function takes result of previous one | |
(value, fn) => fn(value), | |
// reduce's initial value is a left most function's result | |
headFN(...args), | |
); | |
const compose = (...fns) => pipe(...fns.reverse()); | |
/** | |
* Async composition, or Promises composition, or Promises pipe, or pipeP | |
* See https://iamstarkov.com/fp-js-workshop/03-async/#/22 | |
*/ | |
// synchronous composition | |
// function composition (left to right) | |
const pipe = (headFN, ...restFns) => (...args) => restFns.reduce( | |
(value, fn) => fn(value), | |
headFN(...args), | |
); | |
// asynchronous composition | |
// promises composition (left to right) | |
const pipeP = (headPromiseFn, ...restPromiseFns) => (...args) => restPromiseFns.reduce( | |
(promiseValue, promiseFn) => promiseValue.then(promiseFn), | |
headPromiseFn(...args) | |
); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment