尝试使用 Cats Effect Ref monad 以功能方式实现缓存。
为什么内部 Ref 没有按预期设置?
import cats.effect.kernel.Ref
import cats.effect.{IO, IOApp}
object SomeMain extends IOApp.Simple {
val cache: IO[Ref[IO, Option[String]]] = Ref.of[IO, Option[String]](None)
override def run: IO[Unit] = {
val checkValueBeforeSet = cache.flatMap(ref => ref.get.flatMap {
case Some(v) => IO(println(v))
case None => IO(println("as expected no value yet"))
})
val doSetAction = cache.flatMap(ref => ref.set(Some("abc"))).map(_ => println("set action done"))
val checkValueAfterSet = cache.flatMap(ref => ref.get.flatMap {
case Some(v) => IO(println(v))
case None => IO(println("unexpected still no value set!"))
})
for {
_ <- checkValueBeforeSet
_ <- doSetAction
_ <- checkValueAfterSet
} yield IO()
}
}
输出:
as expected no value yet
set action done
unexpected still no value set!
它不起作用,因为您
Ref
每次都在重新创建。这:
是相同的:
(如果你不明白 the 的语义,
IO[A]
你总是可以想象它是() => A
or() => Future[A]
然后它就有意义了)。您在某些辅助方法中使用了
cache
两次(三次),这些方法正在创建本地Ref
,然后让它被遗忘。您必须保留返回的值
cache
并传递它(带有依赖项注入的 wg)才能完成此操作:基本上,
Ref
它是一个包装器AtomicReference
,它在IO
. 它允许安全地改变状态,但它的创建本身是不安全的(每次将其组合到程序中时IO[Ref[]]
都会创建新的Ref
),因此您必须注意如何将其组合到程序中。或者,您可以使用
.memoize
,或使用Ref
某种unsafe
方法创建,但如果您对自己正在做的事情没有很好的直觉,从长远来看,这是一种搬起石头砸自己脚的好方法。