簡體   English   中英

處理條件和Scala中的免費Monad

[英]Handling conditions and Free Monads in Scala

我正在玩CatsFree Monads ,我寫了一個玩具REST服務代數和一個名為ensureOneProduct的“程序”。 不幸的是, ensureOneProduct有更多的鍋爐板代碼,而不是我想看到的。 有沒有更好的方法來編寫下面的ensureOneProduct方法? 或者我剛剛被Haskell的記號所破壞? 謝謝!

import cats.free.Free
import cats.free.Free.liftF
import cats.{Id, ~>}

object Algebra3 {

  type Url = String

  /**
   * The REST Service Algebra
   */
  sealed trait Service[+A]
  case class Get[T](url: Url) extends Service[Option[T]]
  case class Put[T](url: Url, rep: T) extends Service[T]
  case class Post[T](url: Url, rep: T) extends Service[Option[Url]]
  case class Delete(url: Url) extends Service[Unit]

  // A Free REST Service
  type ServiceF[A] = Free[Service, A]

  // The Product resource
  case class Product(name: String, quantity: Int)

  /**
   * Bad example of REST but I'm focusing on learning about Free Monads.
   */
  def ensureOneProduct[T](url: Url, rep: T): ServiceF[Url] = {
    for {
    // Attempt to retrieve the product...
      res <- get[Product](url)
      _ <- if (res.isDefined)
        for {
        // The product existed so delete it.
          _ <- delete(url)
          // Now create the product
          _ <- put(url, rep)
        } yield ()
      else {
        // The product did not exist so create it.
        put(url, rep)
      }
    } yield url
  }

  def get[T](url: Url): ServiceF[Option[T]] = liftF[Service, Option[T]](Get[T](url))
  def put[T](url: Url, rep: T): ServiceF[T] = liftF[Service, T](Put[T](url, rep))
  def post[T](url: Url, value: T): ServiceF[Option[Url]] = liftF[Service, Option[Url]](Post[T](url, value))
  def delete(key: String): ServiceF[Unit] = liftF(Delete(key))

  def defaultCompiler: Service ~> Id =
    new (Service ~> Id) {
      def apply[A](fa: Service[A]): Id[A] =
        fa match {
          case Get(key) =>
            println(s"GET($key)")
            Some(new Product("Hat", 3))
          case Put(key, rep) =>
            println(s"PUT($key, $rep)")
            rep
          case Post(url, rep) =>
            println(s"POST($url)")
            Some(url)
          case Delete(key) =>
            println(s"DELETE($key)")
            ()
        }
    }

  def main(args: Array[String]) = {
    val url = "https://www.example.com/api/v1/hats/1024"
    val product = new Product("Hat", 1)

    println(ensureOneProduct(url, product).foldMap(defaultCompiler))
  }
}

此代碼打印:

GET(https://www.example.com/api/v1/hats/1024)
DELETE(https://www.example.com/api/v1/hats/1024)
PUT(https://www.example.com/api/v1/hats/1024, Product(Hat,1))
https://www.example.com/api/v1/hats/1024

有趣的是有點擔心,當我忘記將嵌套deleteput調用放在for表達式中時,它編譯但沒有運行delete操作。 delete調用被省略是有道理的,但我更願意得到某種編譯時反饋。

您可以使用后跟>> )運算符來廢棄內部以進行理解:

for {
  res <- get[Product](url)               // Attempt to retrieve the product...
  _   <- if (res.isDefined)
           delete(url) >> put(url, rep)  // It exists: delete & recreate it
         else
           put(url, rep)                 // It does not exist: create it
} yield url

暫無
暫無

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

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