I am interested in how to write flow type annotations for such function:
const Box = x => ({
map: f => Box(f(x)),
fold: f => f(x),
});
Type annotations should use Generics, I guess.
Function usage example:
const getRandomColor = (): string =>
Box(Math.random())
.map(x => x * 0xFFFFFF)
.map(Math.floor)
.map(x => x.toString(16))
.fold(x => `#${x.padStart(0, 6)}`);
P.S.: If it is impossible, please write a explanation Why it is impossible.
Answer of @Isitea unfortunately doesn't fit because he changed the source code and that was not the point.
@Jared's answer only gets us part of the way, but beware of the things it's not enforcing.
How we'd like to write it using flow's generics
const Box = <A>(x:A): Box<A> => {
return {
map: <B>(f: A => B): Box<B> => Box(f(x)),
fold: <B>(f: A => B): B => f(x),
}
}
Given a simple function and a value of our box, we should be able to verify that the generic type constraint is enforced
const numberToString = (x: number) : string =>
String (x)
const b : Box<string> =
new Box("1")
b.map(numberToString) // wups! flow doesn't mind!
As of now, this is broken and doesn't work in flow. @mrkev shows us a workaround using a class
class Box<A> {
x: A
constructor (x: A) { this.x = x }
map<B>(f: A => B): Box<B> { return new Box(f(this.x)) }
fold<B>(f: A => B): B { return f(this.x) }
}
const numberToString = (x: number) : string =>
String (x)
const stringToNumber = (x: string): number =>
Number (x)
const b : Box<string> =
new Box("1")
// flow accepts this valid program
b.map(stringToNumber)
// flow rejects this invalid program
b.map(numberToString)
// ^ Cannot call `b.map` with `numberToString` bound to `f` because number [1] is incompatible with string [2] in the first argument.
@thehogfather shares another alternative with a pre-defined type
. This is closer to the original form but requires an exhausting amount of typing - 2x what should be necessary if flow offered a more competent type system. Anyway it works, so we're not allowed to complain.
type Box<A> = {
map: <B>(f: A => B) => Box<B>,
fold: <B>(f: A => B)=> B
}
const box = <A>(x:A): Box<A> => {
return {
map: <B>(f: A => B): Box<B> => box(f(x)),
fold: <B>(f: A => B): B => f(x),
}
}
const numberToString = (x: number) : string =>
String (x)
const stringToNumber = (x: string): number =>
Number (x)
const b : Box<string> =
box("1")
// flow accepts this valid program
b.map(stringToNumber)
// flow rejects this invalid program
b.map(numberToString)
// ^ Cannot call `b.map` with `numberToString` bound to `f` because number [1] is incompatible with string [2] in the first argument.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With