簡體   English   中英

免費Monad的條件行為

[英]Conditional Behavior With Free Monads

我在這里遵循該教程: http : //typelevel.org/cats/datatypes/freemonad.html,並嘗試對其進行修改以與鍵值存儲區前面的緩存一起使用。 到目前為止,這是我想出的,但是我遇到了valueGetOperation的編譯器錯誤。 我了解為什么會收到編譯錯誤,但我只是不了解如何解決該錯誤。 使用免費monad時有條件行為的最佳做法是什么?

import cats.data.Coproduct
import cats.free.{Free, Inject}

object KvStore {
  sealed trait KvOp[A]
  case class Get[T](key: String) extends KvOp[Option[T]]
  case class Put[T](key: String, value: T) extends KvOp[Unit]
  case class Delete[T](key: String) extends KvOp[Unit]
}

object CacheStore {
  sealed trait CacheOp[A]
  case class Get[T](key: String) extends CacheOp[Option[T]]
  case class Put[T](key: String, value: T) extends CacheOp[Unit]
  case class Delete[T](key: String) extends CacheOp[Unit]
}

type WriteThruCache[A] = Coproduct[KvStore.KvOp, CacheStore.CacheOp, A]

class KvOps[F[_]](implicit I: Inject[KvStore.KvOp, F]) {
  import KvStore._
  def get[T](key: String): Free[F, Option[T]] = Free.inject[KvOp, F](Get(key))
  def put[T](key: String, value: T): Free[F, Unit] = Free.inject[KvOp, F](Put(key, value))
  def delete[T](key: String): Free[F, Unit] = Free.inject[KvOp, F](Delete(key))
}

object KvOps {
  implicit def kvOps[F[_]](implicit I: Inject[KvStore.KvOp, F]): KvOps[F] = new KvOps[F]
}

class CacheOps[F[_]](implicit I: Inject[CacheStore.CacheOp, F]) {
  import CacheStore._
  def get[T](key: String): Free[F, Option[T]] = Free.inject[CacheOp, F](Get(key))
  def put[T](key: String, value: T): Free[F, Unit] = Free.inject[CacheOp, F](Put(key, value))
  def delete[T](key: String): Free[F, Unit] = Free.inject[CacheOp, F](Delete(key))
}

object CacheOps {
  implicit def cacheOps[F[_]](implicit I: Inject[CacheStore.CacheOp, F]): CacheOps[F] = new CacheOps[F]
}

def valueWriteOperation[T](implicit Kv: KvOps[WriteThruCache], Cache: CacheOps[WriteThruCache]): ((String, T) => Free[WriteThruCache, Unit]) = {
  (key: String, value: T)  =>
    for {
      _ <- Kv.put(key, value)
      _ <- Cache.put(key, value)
    } yield ()
}

// This is where I'm stuck
// desired behavior: If the value isn't in the cache, load it from the kv store and put it in the cache
def valueGetOperation[T](implicit Kv: KvOps[WriteThruCache], Cache: CacheOps[WriteThruCache]): ((String) => Free[WriteThruCache, Option[T]]) = {
  (key: String) =>
    for {
      cacheOption <- Cache.get[T](key)
      kvOption <- Kv.get[T](key) if cacheOption.isEmpty // value withFilter is not a member of cats.free.Free[A$A39.this.WriteThruCache,Option[T]]
    } yield cacheOption.orElse(kvOption)
}

正如你在知道for理解,當你使用if它是由編譯器脫調用withFilter方法,如果它不能訪問它退到filter方法。 如果未實現它們,您將收到編譯器錯誤。

然而,你可以簡單地使用if else

for {
  booleanValue <- myfreeAlbebra.checkCondidtion(arg1, arg2)
  valueToReturn <- if (booleanValue) {
    myfreeAlbebra.someValue
  } else {
    myfreeAlbebra.someOtherValue
  }
} yield valueToReturn

或者,您可以執行以下操作:

for {
  booleanValue  <- myfreeAlbebra.checkCondidtion(arg1, arg2)
  valueToReturnOpt <- myfreeAlbebra.someValue
  fallbackValue <- myfreeAlbebra.someOtherValue
} yield valueToReturnOpt.getOrElse(fallbackValue)

形式將根據booleanValue將值分配給valueToReturn 這樣,將僅解釋一個分支。 后者將評估兩個值並根據valueToReturnOpt是否為空返回其中之一。

我個人會嘗試類似的方法:

def valueGetOperation[T](implicit Kv: KvOps[WriteThruCache], Cache: CacheOps[WriteThruCache]): ((String) => Free[WriteThruCache, Option[T]]) = {
  (key: String) =>
    for {
      cacheOption <- Cache.get[T](key)
      returnedValue <- if (cacheOption.isEmpty) Cache.get[T](key) else Kv.get[T](key)
    } yield returnedValue
}

遵循Mateusz的建議,這是我想到的:

def withFallback[A[_], T](loadedValue: Option[T], fallback: => Free[A, Option[T]]): Free[A, Option[T]] = {
  if(loadedValue.isDefined) {
    Free.pure[A, Option[T]](loadedValue)
  } else {
    fallback
  }
}

def valueGetOperation[T](implicit Kv: KvOps[WriteThruCache], Cache: CacheOps[WriteThruCache]): ((String) => Free[WriteThruCache, Option[T]]) = {
  (key: String) =>
    for {
      cachedOption <- Cache.get[T](key)
      actualValue <- withFallback[WriteThruCache, T](cachedOption, fallback = Kv.get[T](key))
    } yield actualValue
}

如果有一個標准構造要用Fallback實現,我很高興知道這一點。

您也可以使用OptionT#orElse

import cats.data.OptionT

type KV[A] = Free[WriteThruCache, A]

def valueGetOperation[T](
  implicit
  Kv: KvOps[WriteThruCache], 
  Cache: CacheOps[WriteThruCache]
): String => KV[Option[T]] =
  key => OptionT[KV, T](Cache.get[T](key)).orElse(OptionT[KV, T](Kv.get[T](key))).value

OptionT#orElseF

def valueGetOperation[T](
  implicit
  Kv: KvOps[WriteThruCache], 
  Cache: CacheOps[WriteThruCache]
): String => KV[Option[T]] = 
  key => OptionT[KV, T](Cache.get[T](key)).orElseF(Kv.get[T](key)).value

請注意,在Scala 2.12中使用-Ypartial-unification標志,您不需要KV類型別名,並且可以編寫OptionT(...)而不是OptionT[KV, T](...)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM