fraxture
fraxture

Reputation: 5510

With flow types, is it possible to auto infer return value for function that takes another function as an argument?

Let's say I have a function that takes a function as an argument and returns another function that takes a value that it passes to the function passed as an argument:

const curry = f => x => f(x);

Is it possible to type this function using Flow Type in such a way that the return value of curry is determined by the function f passed in if that function has been typed?

I tried this:

const numToStr = (x: number): string => x.toString();
const curry = <F: Function>(f: F): (mixed => *) => (x: mixed): * => f(x);
const curried = curry(numToStr);

link

The result of passing the first argument is that the resulting function curried has a signature like this:

(((x: mixed) => any) | ((x: mixed) => empty))

This makes sense, either the result is empty or it's anything. What I'd hoped is that because numToStr is typed, that it'd be possible for Flow to understand that curried is really number => string.

Is there a way to do this that I've missed? If not, can anyone explain why.

Upvotes: 2

Views: 185

Answers (1)

Peter Hall
Peter Hall

Reputation: 58725

Currying usually applies to the process of taking a function of two arguments and transforming it into a function of one argument that returns another function of one argument which returns the result.

In Flow it would look like this:

const curry = <A, B, C>(f: (A, B) => C): (A => (B => C)) => (x) => (y) => f(x, y);

const prefixNum = (prefix: string, x: number): string => prefix + x.toString();
const prefixed = curry(prefixNum);
const withHello = prefixed("hello ");
const result = withHello(3); // "hello 3"

Your version is a bit different. You are transforming a function of one argument into a function of zero arguments. This is more like lazy evaluation than currying: supply all of the arguments but only return a zero-argument function (sometimes called a "thunk"), which actually performs the computation when you call it. The implementation could look like this:

const makeLazy = <A, C>(f: A => C): (A => (() => C)) => (x) => () => f(x);

It's essentially the definition above, but with one of the types replaced with (). This should work how you want it to:

const numToStr = (x: number): string => x.toString();
const lazyNumToStr = makeLazy(numToStr);
const thunk = lazyNumToStr(3);
thunk(); // "3"

Upvotes: 1

Related Questions