The stable-memo package

Whereas most memo combinators memoize based on equality, stable-memo
does it based on whether the exact same argument has been passed to
the function before (that is, is the same argument in memory).

stable-memo only evaluates keys to WHNF.

This can be more suitable for recursive functions over graphs with
cycles.

stable-memo doesn't retain the keys it has seen so far, which
allows them to be garbage collected if they will no longer be
used. Finalizers are put in place to remove the corresponding
entries from the memo table if this happens.

Data.StableMemo.Weak provides an alternative set of combinators
that also avoid retaining the results of the function, only
reusing results if they have not yet been garbage collected.

There is no type class constraint on the function's argument.

stable-memo will not work for arguments which happen to have the
same value but are not the same heap object. This rules out many
candidates for memoization, such as the most common example, the
naive Fibonacci implementation whose domain is machine Ints; it can
still be made to work for some domains, though, such as the lazy
naturals.

Below is an implementation of map that preserves sharing of the
spine for cyclic lists. It should even be safe to use this on
arbitrarily long, acyclic lists since as long as the garbage
collector is chasing you, the size of the memo table should stay
under control, too.