-
-
Notifications
You must be signed in to change notification settings - Fork 150
/
trampoline.ts
55 lines (53 loc) · 1.38 KB
/
trampoline.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
import type { Fn0, Fn0A, MaybePromise } from "@thi.ng/api";
/**
* Takes a function returning either a no-arg function (thunk) or its
* already realized (non-function) result. Re-executes thunk for as long
* as it returns another function/thunk. Once a non-function result has
* been produced, `trampoline` returns that value itself.
*
* @remarks
* If the final result should be function, it needs to wrapped (e.g. as
* a 1-elem array).
*
* This function should be used for non-stack consuming recursion. I.e.
* a trampoline is a form of continuation passing style and only ever
* consumes max. 2 extra stack frames, independent from recursion depth.
*
* @example
* ```ts tangle:../export/trampoline.ts
* import { trampoline } from "@thi.ng/compose";
*
* const countdown = (acc, x) =>
* x >= 0 ?
* () => (acc.push(x), countdown(acc, x-1)) :
* acc;
*
* trampoline(countdown([], 4))
* // [ 4, 3, 2, 1, 0 ]
*
* trampoline(countdown([], -1))
* // []
* ```
*
* @param f - function
*/
export const trampoline = <T>(f: T | Fn0<T | Fn0<T>>) => {
while (typeof f === "function") {
f = (<any>f)();
}
return <T>f;
};
/**
* Async version of {@link trampoline}.
*
* @param f - function
*/
export const trampolineAsync = async <T>(
f: MaybePromise<T | Fn0A<T | Fn0A<T>>>
) => {
f = await f;
while (typeof f === "function") {
f = await (<any>f)();
}
return <T>f;
};