2

Define

def memoizeCoeval(n: Int): Coeval[Int] = {
  if (n <= 1)
    Coeval.now(1)
  else
    Coeval.defer(memoizeCoeval(n - 1)).map(_ + 1).memoize
}

Now

memoizeCoeval(10000).value

blows the stack. If we remove the .memoize from the recursive call, it works (as expected). Why?

Kamil Kloch
  • 333
  • 1
  • 9

2 Answers2

0

It is shortcoming of Coeval, you may use Eval instead of it for stack-safe recursive memoized computations in the same manner.

xrtm000
  • 91
  • 1
  • 5
0

Without memoize, you get a Coeval which is a composition of Suspends. Each Suspend contains a thunk that is a function that returns the next Suspend: the run loop unrolls that by calling apply on the thunk, setting the resulting Coeval as the current one, and then continuing the loop.

When using memoize, the Suspend contains a LazyVal which extends () -> Coveal. It overrides apply in a way that calls directly apply on its own underlying thunk, which in turn calls the run loop. So when LazyVal’s apply is called in the run loop, it calls the run loop recursively, which causes the stack overflow.

I don’t know if it’s a bug as Coeval is meant to be stack safe, but note that it doesn’t make sense to memoize each step of your recursive method as the corresponding Coveal will never be reused. You probably want to memoize only the final result.

WilQu
  • 7,131
  • 6
  • 30
  • 38