Skip to content

Instantly share code, notes, and snippets.

@fizzy33
Created May 3, 2021 12:21
Show Gist options
  • Save fizzy33/133c4a9e381de1315623be4cd99804b3 to your computer and use it in GitHub Desktop.
Save fizzy33/133c4a9e381de1315623be4cd99804b3 to your computer and use it in GitHub Desktop.
import cats.effect._
import cats.effect.implicits._
import cats._
import cats.effect.kernel.Ref.Make
import cats.implicits._
import scala.concurrent.duration.FiniteDuration
import scala.language.higherKinds
object Cache {
/**
* Memoizes generator then after the forgetAfter times out it and will wash rinse and repeat.
* Any errors that happen in delegate bubble up. So if recovery or other things are wanted
* bake that into the delegate passed in.
*
* We assume generator is idempotent
*
*/
def apply[F[_] : FlatMap : Monad : Clock, A](generator: F[A], forgetAfter: FiniteDuration)(implicit mk: Make[F]): F[F[A]] = {
for {
ref <- Ref[F].of(none[(A, FiniteDuration)])
} yield impl.memoizeAndForgetViaRef(generator, forgetAfter, ref)
}
object impl {
/**
* A separate method for using a provided ref to do the heavy lifting.
* We are okay with race conditions (last one overwrites)
*/
def memoizeAndForgetViaRef[F[_] : FlatMap : Monad : Clock, A](generator: F[A], forgetAfter: FiniteDuration, ref: Ref[F,Option[(A,FiniteDuration)]])(implicit mk: Make[F]): F[A] = {
val F = Monad[F]
for {
refValue <- ref.get
currentTime <- Clock[F].realTime // we don't really care if the currentTime drifts a bit we are going to assume forgetAfter time is significantly large than time to run delegate
memoizedValue <-
refValue match {
case Some((v, rememberUntilTime)) =>
if (currentTime < rememberUntilTime)
F.pure(Some(v))
else
ref.set(None).map(_ => None)
case None =>
F.pure(None)
}
resolvedValue <-
memoizedValue match {
case None =>
for {
v <- generator
currentTime <- Clock[F].realTime
_ <- ref.set(Some(v, currentTime + forgetAfter))
} yield v
case Some(v) =>
F.pure(v)
}
} yield resolvedValue
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment