-
Notifications
You must be signed in to change notification settings - Fork 1.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Annotation for compose
function on flowtype: compose(m1, m2, m3)(...args) -> m1(m2(m3(...args)))
#1950
Comments
Hey @nodkz ... Hope this helps! |
The gist of it: It is only possible to type this by manually writing compose declarations for a function call with 1, 2, 3,... n parameters... assuming that your users don't use a higher number than |
@ryyppy you are crazy magician! ;) PS. I want use it for graphql-compose. There must be 3 compositions (resolve methods, fields args and object types), which all works via this Yesterday with tears in my eyes ;) I splice my lib with |
May be somebody suggest better solution? |
@nodkz For clarification, I did not write this code, I am just vary of its existence :-) |
@ryyppy no problem! Thanks again. Gabe Levi provide his solution: (provide it here for future travelers from search engines): /*
CAUTION
this solution has wrong order annotation.
It works for `reduce`, but in this compose function using `reduceRight`.
Proper solution can be founв on next comment.
*/
/* @flow */
function composeImpl(...funcs) {
if (funcs.length === 0) {
return arg => arg;
}
const last = funcs[funcs.length - 1];
const rest = funcs.slice(0, -1);
return (...args) => rest.reduceRight((composed, f) => f(composed), last(...args));
}
var compose:
((end: void) => (<T>(x: T) => T)) &
(<Ta, Tb>(f: (x: Ta) => Tb, end: void) => ((x: Ta) => Tb)) &
(<Ta, Tb, Tc>(f1: (x: Ta) => Tb, f2: (x: Tb) => Tc, end: void) => ((x: Ta) => Tc)) &
(<Ta, Tb, Tc, Td>(f1: (x: Ta) => Tb, f2: (x: Tb) => Tc, f3: (x: Tc) => Td, end: void) => ((x: Ta) => Td)) &
(<T>(...funcs: Array<(x: T) => T>) => ((x: T) => T))
= (composeImpl: any);
type ERROR = boolean
// Explicitly handled cases work
(compose()(123) : ERROR); // number ~> boolean
(compose(String)(123) : ERROR); // string ~> boolean
(compose(String, Number)(123) : ERROR); // number ~> boolean
(compose(String, Number, String)(123) : ERROR); // string ~> boolean
// And fallback requires all the functions to take and return the same type
// which is in this case string | number
(compose(String, Number, String, Number)(123) : string | number); |
Correct fully tested solution (combined from @ryyppy and @gabelevi) /* @flow */
/* eslint-disable */
/**
* Composes single-argument functions from right to left. The rightmost
* function can take multiple arguments as it provides the signature for
* the resulting composite function.
*
* @param {...Function} funcs The functions to compose.
* @returns {Function} A function obtained by composing the argument functions
* from right to left. For example, compose(f, g, h) is identical to doing
* (...args) => f(g(h(...args))).
*/
function composeImpl(...funcs) {
if (funcs.length === 0) {
return arg => arg;
}
const last = funcs[funcs.length - 1];
const rest = funcs.slice(0, -1);
return (...args) => rest.reduceRight((composed, f) => f(composed), last(...args));
}
type FN<A,R> = (a: A) => R;
const compose:
((end: void) => (<T>(x: T) => T)) &
(<A,B>(m1: FN<A,B>, end: void) => FN<A,B>) &
(<A,B,C>(m1: FN<B,C>, m2: FN<A,B>, end: void) => FN<A,C>) &
(<A,B,C,D>(m1: FN<C,D>, m2: FN<B,C>, m3: FN<A,B>, end: void) => FN<A,D>) &
(<A,B,C,D,E>(m1: FN<D,E>, m2: FN<C,D>, m3: FN<B,C>, m4: FN<A,B>, end: void) => FN<A,E>) &
(<A,B,C,D,E,F>(m1: FN<E,F>, m2: FN<D,E>, m3: FN<C,D>, m4: FN<B,C>, m5: FN<A,B>, end: void) => FN<A,F>) &
(<A,B,C,D,E,F,G>(m1: FN<F,G>, m2: FN<E,F>, m3: FN<D,E>, m4: FN<C,D>, m5: FN<B,C>, m6: FN<A,B>, end: void) => FN<A,G>) &
(<A,B,C,D,E,F,G,H>(m1: FN<G,H>, m2: FN<F,G>, m3: FN<E,F>, m4: FN<D,E>, m5: FN<C,D>, m6: FN<B,C>, m7: FN<A,B>, end: void) => FN<A,H>) &
(<A,B,C,D,E,F,G,H,J>(m1: FN<G,J>, m2: FN<G,H>, m3: FN<F,G>, m4: FN<E,F>, m5: FN<D,E>, m6: FN<C,D>, m7: FN<B,C>, m8: FN<A,B>, end: void) => FN<A,J>) &
(<A,B,C,D,E,F,G,H,J,K>(m1: FN<J,K>, m2: FN<G,J>, m3: FN<G,H>, m4: FN<F,G>, m5: FN<E,F>, m6: FN<D,E>, m7: FN<C,D>, m8: FN<B,C>, m9: FN<A,B>, end: void) => FN<A,K>) &
(<A,B,C,D,E,F,G,H,J,K,L>(m1: FN<K,L>, m2: FN<J,K>, m3: FN<G,J>, m4: FN<G,H>, m5: FN<F,G>, m6: FN<E,F>, m7: FN<D,E>, m8: FN<C,D>, m9: FN<B,C>, m10: FN<A,B>, end: void) => FN<A,L>) &
(<A,R>(...funcs: Array<FN<A,R>>) => FN<A,R>)
= (composeImpl: any);
export default compose; |
compose
function on flowtype: compose(m1, m2, m3)(...args) -> m1(m2(m3(...args)))
How to annotate
compose
function, that will be covered on 100%. This function is polymorphic (it can accept different function types and different arguments).The text was updated successfully, but these errors were encountered: