CCCPaste

HELLO JAVASCRIPT

// trampoline : (thunk -> thunk | value) -> value
// Runs a chain of thunks iteratively so recursion doesn't grow the JS call stack.
// - Start with a "thunk" (a function with no args)
// - While the result is still a function, keep calling it
// - When a non-function value is produced, return it
const trampoline = (thunk) => {
  let t = thunk;
  while (typeof t === "function") t = t(); // keep "bouncing"
  return t; // final value
};

// foldl : (f, acc, xs, k) -> ...
//
// Fold an array xs from left to right in a continuation-passing style (CPS) and
// use trampoline to stay stack-safe.
//
// Type-ish comments:
// - f : (x, acc, k2) => ...
//   where k2 : (newAcc) => ... is a continuation that tells foldl what to do next
//   IMPORTANT: f is expected to call k2(newAcc) rather than directly returning newAcc.
// - k : (finalAcc) => ... is the final continuation invoked at the end.
//
// Returns: a call that eventually triggers k(finalAcc).
const foldl = (f, acc, xs, k) => {
  // step(i, accNow) describes "the rest of the fold" starting at index i
  // and current accumulator accNow.
  //
  // It returns either:
  // - a thunk (function) that when called computes the next step, or
  // - in the base case, a thunk that calls k(accNow).
  const step = (i, accNow) => {
    // Base case: we've processed every element in xs.
    if (i >= xs.length) return () => k(accNow);

    // Process the next element.
    const x = xs[i];

    // We don't call f(x, accNow, ...) immediately on the call stack.
    // Instead, we return a thunk so trampoline can manage the iteration safely.
    return () =>
      // Call f in CPS form:
      // f decides how to compute/update the accumulator, and then calls k2(newAcc).
      f(x, accNow, (newAcc) => {
        // After f computes newAcc, continue folding from i+1.
        // step(i + 1, newAcc) returns a thunk, which trampoline will eventually run.
        return step(i + 1, newAcc);
      });
  };

  // Start the fold at index 0 with the initial accumulator acc.
  // step(0, acc) returns the initial thunk; trampoline runs it to completion.
  return trampoline(() => step(0, acc));
};