-
-
Notifications
You must be signed in to change notification settings - Fork 94
Description
Update (2017-10-03): I have updated chainCurriedFlipped to delay the introduction of B, as suggested by @gcanti. I have also updated the results to correct an unrelated error I made initially (providing arguments to chainCurriedFlipped in the wrong order).
@miangraham and I have been working on #431 for several weeks. We've made good progress despite the impedance mismatch between the two type systems.
I had trouble defining types for chain. To simplify the task I specialized the type signature:
chain :: (a -> Array b) -> Array a -> Array bI couldn't get this to work. I asked for help in the TypeScript room on Gitter and @sluukkonen provided helpful responses (including a link to microsoft/TypeScript#17520). I learnt that certain combinations of removing currying, changing the argument order, and replacing id with x => x (!) would appease the compiler.
I defined four versions of “chain” to discover the magic combination:
const xss = [[1, 2], [3, 4], [5, 6]];
function id<A>(x: A): A { return x; }
function chain<A, B>(f: (x: A) => Array<B>, xs: Array<A>): Array<B> {
return Z.chain(f, xs);
}
function chainFlipped<A, B>(xs: Array<A>, f: (x: A) => Array<B>): Array<B> {
return Z.chain(f, xs);
}
function chainCurried<A, B>(f: (x: A) => Array<B>): (xs: Array<A>) => Array<B> {
return xs => Z.chain(f, xs);
}
function chainCurriedFlipped<A>(xs: Array<A>): <B>(f: (x: A) => Array<B>) => Array<B> {
return f => Z.chain(f, xs);
}Results of applying functions to id and xss
| Function | Result | Value/Explanation |
|---|---|---|
chain |
❌ | Argument of type <A>(x: A) => A is not assignable to parameter of type (x: {}) => {}[]. |
chainFlipped |
✅ | [1, 2, 3, 4, 5, 6] |
chainCurried |
❌ | Argument of type <A>(x: A) => A is not assignable to parameter of type (x: {}) => {}[]. |
chainCurriedFlipped |
✅ | [1, 2, 3, 4, 5, 6] |
Results of applying functions to xs => xs and xss
| Function | Result | Value/Explanation |
|---|---|---|
chain |
✅ | [1, 2, 3, 4, 5, 6] |
chainFlipped |
✅ | [1, 2, 3, 4, 5, 6] |
chainCurried |
❌ | Argument of type (xs: {}) => {} is not assignable to parameter of type (x: {}) => {}[]. |
chainCurriedFlipped |
✅ | [1, 2, 3, 4, 5, 6] |
My reactions to the tables above:
- 😱
idandxs => xsare not equivalent - 😞 TypeScript does not like curried functions
- 😠 TypeScript's limitations affect API design
I'm concerned that even if we succeed in approximating type classes with interfaces, complications such as these will make Sanctuary and TypeScript an awkward couple. What do others think?
Sanctuary was designed as a JavaScript library. Had we set out to create a functional programming library for TypeScript it's likely we would have done things quite differently. I'm concerned that treating both JavaScript and TypeScript as first-class targets will lead to compromises. I'd like to continue to focus on making Sanctuary a principled, capable JavaScript library. Perhaps the TypeScript type definitions should live elsewhere so TypeScript's limitations won't influence the design of our API.