5 ms·
This is a valid definition in TypeScript: type Shape<T> = ( circle: (x: number, y: number, r: number) => T, rectangle: (x: number, y: numbe
by gampleman 6y ago
This is a valid definition in TypeScript:
type Shape<T> = (
circle: (x: number, y: number, r: number) => T,
rectangle: (x: number, y: number, w: number, h: number) => T
) => T;
function Circle<T>(x: number, y: number, r: number): Shape<T> {
return (_Circle, _Rectangle) => _Circle(x, y, r);
}
function Rectangle<T>(x: number, y: number, w: number, h: number): Shape<T> {
return (_Circle, _Rectangle) => _Rectangle(x, y, w, h);
}
const exampleCircle = Circle(2, 1.4, 4.5);
const exampleRectangle = Rectangle(1.3, 3.1, 10.3, 7.7);
function area(shape: Shape<unknown>): number {
const s = shape as Shape<number>;
return s(
(x, y, r) => Math.PI * r * r,
(x, y, w, h) => w * h
);
}
export function main() {
console.log(area(exampleCircle));
console.log(area(exampleRectangle));
}
Unfortunately I think the cast might be necessary, but happy to see another solution from someone with more typescript expertise.
- magicalhippo 6y agoThanks, that helps a lot! Never used TypeScript either (I feel like such a Luddite these days) but this is perfectly understandable.
- DougBTX 6y agoIt works without the cast if Shape is a generic function itself: type Shape = <T>( circle: (x: number, y: number, r: number) => T, rectangle: (x: number, y: number, w: number, h: number) => T ) => T; function Circle(x: number, y: number, r: number): Shape { return (_Circle, _Rectangle) => _Circle(x, y, r); } function Rectangle(x: number, y: number, w: number, h: number): Shape { return (_Circle, _Rectangle) => _Rectangle(x, y, w, h); } const exampleCircle = Circle(2, 1.4, 4.5); const exampleRectangle = Rectangle(1.3, 3.1, 10.3, 7.7); function area(shape: Shape): number { return shape( (x, y, r) => Math.PI * r * r, (x, y, w, h) => w * h ); } console.log(area(exampleCircle)); console.log(area(exampleRectangle));
- throw_m239339 6y agoI'm embarrassed to admit it took me a while to make sense of that code snippet. Now, in order for it to work, a language has to support closures, not just functions.
- Twisol 6y ago> Now, in order for it to work, a language has to support closures, not just functions. Objects are a reasonable stand-in for closures. Pass in the first batch of arguments to the constructor, and store them as fields. Access them from the method (which plays the role of the "returned" closure) when it's called later. (This is effectively the same as "closure conversion" in the literature, but we're taking advantage of the implicit-receiver convention to hide the parameter by which we're passing the prepared fields.)
- lmm 6y agoYou just need to make the function generic rather than the type: type Shape = <T extends unknown>( circle: (x: number, y: number, r: number) => T, rectangle: (x: number, y: number, w: number, h: number) => T ) => T; function Circle<T>(x: number, y: number, r: number): Shape { return (_Circle, _Rectangle) => _Circle(x, y, r); } function Rectangle<T>(x: number, y: number, w: number, h: number): Shape { return (_Circle, _Rectangle) => _Rectangle(x, y, w, h); } const exampleCircle = Circle(2, 1.4, 4.5); const exampleRectangle = Rectangle(1.3, 3.1, 10.3, 7.7); function area(shape: Shape): number { return shape( (_x, _y, r) => Math.PI * r * r, (_x, _y, w, h) => w * h ); } function main() { console.log(area(exampleCircle)); console.log(area(exampleRectangle)); } main()
- monzee 6y agoYou can generalize this with TS's fancy mapped types: type Sum<K> = <T> (cases: Pattern<K, T>) => T type Pattern<K, T> = { [V in keyof K]: K[V] extends any[] ? (...args: K[V]) => T : never } which takes TS very close to ML: type Shape = Sum<{ circle: [number, number, number], rectangle: [number, number, number, number] }> function Circle(x: number, y: number, r: number): Shape { return ({ circle }) => circle(x, y, r); } function Rectangle(x: number, y: number, w: number, h: number): Shape { return ({ rectangle }) => rectangle(x, y, w, h); } function area(shape: Shape): number { return shape({ circle: (x, y, r) => Math.PI * r * r, rectangle: (x, y, w, h) => w * h }); }