Skip to content

Commit

Permalink
feat(compose): add trampoline()
Browse files Browse the repository at this point in the history
  • Loading branch information
postspectacular committed Mar 8, 2019
1 parent 3707e61 commit 9e4c171
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 0 deletions.
1 change: 1 addition & 0 deletions packages/compose/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ export * from "./juxt";
export * from "./partial";
export * from "./thread-first";
export * from "./thread-last";
export * from "./trampoline";
35 changes: 35 additions & 0 deletions packages/compose/src/trampoline.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { Fn0 } 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. 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.
*
* ```
* 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
*/
export const trampoline = <T>(f: T | Fn0<T | Fn0<T>>) => {
while (typeof f === "function") {
f = (<any>f)();
}
return <T>f;
};

0 comments on commit 9e4c171

Please sign in to comment.