Flow: Typecheck `compose` function

Created on 1 Mar 2016  路  25Comments  路  Source: facebook/flow

Just to avoid ambiguity: The compose function is a function that satisfies compose(f, g, h)(...args) == f(g(h(...args))).

How can I type check the compose function implementation with Flow? Since it could receive an arbitrary number of args, I was thinking on providing different signatures and provide types of all of them up to, say, 5 arguments, but not sure about the syntax for that.

This is what I tried, but it's not working:

declare function compose<T1>(
  f: (...args: any) => T1
): (...args: any) => T1

declare function compose<T1, T2>(
  f: (x: T1) => T2,
  g: (...args: any) => T1
): (x: T1) => T2

//... up to 5 args

export default function compose(...args) {
 // function implementation
}

Another approach that didn't work either was:

type Compose1<T1> = (
  f: (...args: any) => T1
) => (...args: any) => T1

type Compose2<T1, T2> = (
  f: (x: T1) => T2,
  g: (...args: any) => T1
) => (x: T1) => T2

//... up to 5 args

type Compose = Compose1 | Compose2 | Compose3 | Compose4 | Compose5;

const compose : Compose = (...funcs) => {
 // function impl
}

export default compose;

Any hints? Is this even supported?

All 25 comments

@leoasis Take a look at this typescript interfaces for ramda. There is a compose function that you can base your approach on. They use function overloading while as far as I understand it's recommended to use intersection type for flow. In fact you were close but you used union type for your composes

Looks like that compose and similar functions with multiple typed arguments, is not possible declare in flow.
@leoasis Have you found any solution?

@istarkov check out ramda typings

@alexeygolev Why do u write about rambda typings as typescript and flow is not the same things.

This and similar will not work in flow

        compose<V0, T1, T2>(fn1: (x: T1) => T2, fn0: (x0: V0) => T1): (x0: V0) => T2;
        compose<V0, V1, T1, T2>(fn1: (x: T1) => T2, fn0: (x0: V0, x1: V1) => T1): (x0: V0, x1: V1) => T2;
        compose<V0, V1, V2, T1, T2>(fn1: (x: T1) => T2, fn0: (x0: V0, x1: V1, x2: V2) => T1): (x0: V0, x1: V1, x2: V2) => T2;

as flow just get the first definition,
also no luck with union/intersection types.

@istarkov I meant this

@alexeygolev Thank you, will try

Looks like even ...rest: Array<void> should not help,
here is simple non working example

declare function test<A, B>(a: A, b: B, ...rest: Array<void>) : B;
declare function test<A>(a: A, ...rest: Array<void>) : A;

const xxxx = test(1, 2); // flow detects that xxxx is a number
const yyyy = test(1); // flow detects that yyyy is void, as 1 declare used 

oops this work

declare function test<A>(a: A, ...rest: Array<void>) : A;
declare function test<A, B>(a: A, b: B, ...rest: Array<void>) : B;

const xxxx = test(1, 2);
const yyyy = test(1);

@alexeygolev Thank you a lot, all works fine!!!!

@istarkov Let's first decide if we're talking about variadics in general or compose in particular

@istarkov look through my tests for ramda in the same repo) might give you some more examples

Looks like till now I start to live in ramda repo ;-)

Nice! will check that out too then!

To be clear and to double-check my understanding: Flow doesn't currently support a way to make a correct type declaration for a compose function that can take any number of functions, right? I think that'd be a great feature for Flow.

@AgentME do you know any type system (not only for JS) that can do it?
Just to have a reference point

@alexeygolev No, I don't.

In https://github.com/facebook/flow/issues/1251#issuecomment-244284732, I proposed that the compose function might be popular enough to warrant its own special type like Promise.all currently has, and I also proposed an alternate more general solution involving a template with a special reduce type function that operates on the parameter types which could also be used to type functions like Promise.all.

You should use ...rest: [] instead of ...rest: Array<void>.

The latter allows undefined which can screw up arguments.length.

Here is how you would type compose():

function compose<TValue, TArgs: Iterable<mixed>>(fn: (...TArgs) => TValue, ...args: TArgs): TValue {
  return fn(...args);
}

function myFn(a: number, b: string): string {
  return a + b;
}

const x: empty = compose(myFn, 'nope', 42);

Note that this would not work in TypeScript 馃槈

https://flow.org/try/#0GYVwdgxgLglg9mABBOBbADnAzgUwDwAqAagIYA2IOANIgQIIBOA5lgFyICSUODJARmXyoYADxwATAHySAFMDDsZAOhX1mWAJSIAvJNqkK1RCqUl17NSw0WDlRAG8AUIkQMcUEAyTzlKs1YBuRwBfR0dQSFgERFQATwAxMBkSdjAQVD4eGj52LCgGGDAma0Q8gqKHZ1d3TyQSRABqRD4g0McUMDzEEXYcDChYnWQ0TFwZOMSaAHIwOHQcKZoAFgAmDSCgA

@calebmer Sorry, that's a different compose function than this thread is about. This thread is about the compose function as defined in libraries like Ramda, transducers.js, and Lodash (as "flowRight"), which takes multiple functions and combines them into one function which calls all the given functions while passing each one's result as the next one's parameter.

R.compose(x => x+0.2, x => x*10, x => x+1)(5);
// 60.2

@calebmer better to use ramda compose typedef from flowtyped, mixed is bad idea, type inference would not work,
as an example recompose https://github.com/acdlite/recompose/tree/master/types use it

BTW I found strange behavior of compose https://github.com/facebook/flow/issues/4342
Some errors not detected if compose defined in libdef file

@AgentME sorry for the misunderstanding 馃様

I recommend using function overloading in this case as is suggested by @istarkov. I鈥檓 going to be looking at typing Redux soon and if it makes sense then we may add a special case for compose().

@istarkov I鈥檒l comment in #4342 馃憤

Hey @calebmer did you end up with a solution for the compose use case?

@FezVrasta $Compose and $ComposeReverse exist probably about ten versions.

Oh thanks, sorry I did miss that.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

cubika picture cubika  路  3Comments

ghost picture ghost  路  3Comments

ctrlplusb picture ctrlplusb  路  3Comments

doberkofler picture doberkofler  路  3Comments

pelotom picture pelotom  路  3Comments