2017-05-11 5 views
1

Je joue avec Cats et Free Monads et j'ai écrit une algèbre de service REST jouet et un "programme" appelé ensureOneProduct. Malheureusement, ensureOneProduct a plus de code plaque de chaudière que je voudrais voir. Existe-t-il un meilleur moyen d'écrire la méthode ensureOneProduct ci-dessous? Ou ai-je été gâté par la notation Haskell's? Merci!Conditions de traitement et Monades gratuites dans Scala

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)) 
    } 
} 

Ce code imprime:

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 

Il était intéressant et un peu au sujet que lorsque j'ai oublié de joindre les delete et put imbrications dans l'expression, il a compilé mais n'a pas couru la delete opération. Il est logique pourquoi l'appel delete a été omis, mais je préférerais obtenir une sorte de retour de la compilation.

Répondre

3

Vous pourriez être en mesure de mettre au rebut l'intérieur pour la compréhension à l'aide du suivie (>>) opérateur:

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 
+0

Tout ce que je devais faire était d'ajouter: cats.implicits._ d'importation et qui a très bien fonctionné. –