使用 Cats Effect Ref 作为缓存

问题描述 投票:0回答:1

尝试使用 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!
scala functional-programming cats-effect
1个回答
2
投票

它不起作用,因为您每次都重新创建

Ref

这个:

val cache: IO[Ref[IO, Option[String]]] = Ref.of[IO, Option[String]](None)

等同于:

val cache: () => AtomicReference[Option[String]] =
  () => new AtomicReference(None)

(如果你不明白

IO[A]
的语义,你总是可以想象它是
() => A
() => Future[A]
,然后它就有意义了)。

您在某些辅助方法中使用了

cache
两次(三次),这些方法正在创建本地
Ref
,然后让它被遗忘。

您必须保留

cache
返回的值并传递它(例如,使用依赖注入)才能完成这项工作:

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] = cache.flatMap { ref =>

    val checkValueBeforeSet = ref.get.flatMap {
      case Some(v) => IO(println(v))
      case None => IO(println("as expected no value yet"))
    }

    val doSetAction = ref.set(Some("abc"))).map(_ => println("set action done")

    val checkValueAfterSet = ref.get.flatMap {
      case Some(v) => IO(println(v))
      case None => IO(println("unexpected still no value set!"))
    }

    for {
      _ <- checkValueBeforeSet
      _ <- doSetAction
      _ <- checkValueAfterSet
    } yield IO()
  }
}

基本上,

Ref
AtomicReference
的包装器,它在
IO
中运行每个操作。它可以安全地改变状态,但它的创建本身是不安全的(每次将其组合到程序中时
IO[Ref[]]
都会创建新的
Ref
),因此您必须注意如何将其组合到程序中。

或者,您可以使用

.memoize
,或者使用一些
Ref
方法创建
unsafe
,但从长远来看,如果您没有很好的直觉,那么这是一个搬起石头砸自己脚的好方法,如果您没有很好的直觉正在做。

© www.soinside.com 2019 - 2024. All rights reserved.