The Memo effect allows the caching of expensive computations. Computations are “stored” with a given key, so that the next computation with the same key will return the previously computed value. When interpreting those computations a Cache must be provided:

import cats.Eval
import cats.implicits._
import org.atnos.eff._, memo._
import org.atnos.eff.syntax.memo._
import org.atnos.eff.syntax.eval._
import org.atnos.eff.syntax.eff._

type S = Fx.fx2[Memoized, Eval]

var i = 0

def expensive[R :_memo]: Eff[R, Int] =
  memoize("key", { i += 1; 10 * 10 })

(expensive[S] >> expensive[S]).runMemo(ConcurrentHashMapCache()) === 100

"there is only one invocation" <==> (i === 1)

> there is only one invocation <=> true

There are 2 cache implementations provided in this library to support the Memo effect:

You can also use other, and better, cache implementations like Caffeine to get more functionalities like eviction policies, maximum size and so on. You will need to implement the Cache interface for this

trait Cache {
  def memo[V](key: AnyRef, value: =>V): V