Используйте Cats Effect Ref в качестве кеша

Попытка реализовать кеширование функциональным способом с использованием монады Cats Effect 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!
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
0
130
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Это не работает, потому что вы каждый раз создаёте 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, но это отличный способ выстрелить себе в ногу в долгосрочной перспективе, если у вас не очень хорошая интуиция, что вы делаете.

Другие вопросы по теме