// tracing: off
import * as Tp from "../Collections/Immutable/Tuple"
import { pipe } from "../Function"
import * as P from "../Promise"
import * as RefM from "../RefM"
import { fork, succeed } from "./core"
import * as Do from "./do"
import * as map from "./map"
import * as tap from "./tap"
import * as to from "./to"
/**
 * Returns a memoized version of the specified effectual function.
 */

export function memoize(f, __trace) {
  return map.map_(
    RefM.makeRefM(new Map()),
    (ref) => (a) =>
      map.map_(
        Do.bind_(
          Do.bind_(Do.do, "promise", () =>
            RefM.modify((m) => {
              const memo = m.get(a)

              if (memo) {
                return succeed(Tp.tuple(memo, m))
              }

              return map.map_(
                tap.tap_(
                  Do.bind_(Do.do, "promise", () => P.make()),
                  ({ promise }) => fork(to.to_(f(a), promise))
                ),
                ({ promise }) => Tp.tuple(promise, m.set(a, promise))
              )
            })(ref)
          ),
          "b",
          ({ promise }) => P.await(promise)
        ),
        ({ b }) => b
      ),
    __trace
  )
}
/**
 * Returns a memoized version of the specified effectual function.
 *
 * This variant uses the compare function to compare `A`
 */

export function memoizeEq(compare) {
  return (f) =>
    map.map_(
      RefM.makeRefM(new Map()),
      (ref) => (a) =>
        map.map_(
          Do.bind_(
            Do.bind_(Do.do, "promise", () =>
              RefM.modify((m) => {
                for (const [k, v] of m) {
                  if (compare(k)(a)) {
                    return succeed(Tp.tuple(v, m))
                  }
                }

                return map.map_(
                  tap.tap_(
                    Do.bind_(Do.do, "promise", () => P.make()),
                    ({ promise }) => fork(to.to_(f(a), promise))
                  ),
                  ({ promise }) => Tp.tuple(promise, m.set(a, promise))
                )
              })(ref)
            ),
            "b",
            ({ promise }) => P.await(promise)
          ),
          ({ b }) => b
        )
    )
}
//# sourceMappingURL=memoize.js.map
