Ich spiele mit Cats und Free Monads und ich habe eine Spielzeug REST Service Algebra und ein "Programm" namens ensureOneProduct
geschrieben. Leider hat ensureOneProduct
mehr Kesselplattencode, als ich gerne sehen würde. Gibt es eine bessere Möglichkeit, die ensureOneProduct
Methode unten zu schreiben? Oder habe ich gerade verwöhnt von Haskell's Do Notation? Vielen Dank!Handling Bedingungen und kostenlose Monaden in 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))
}
}
Dieser Code druckt:
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
Es war interessant und ein wenig über das, wenn ich die verschachtelten delete
und put
Anrufe in eine für den Ausdruck zu umschließen vergessen, kompiliert es aber laufen nicht die delete
Betrieb. Es macht Sinn, warum der Aufruf delete
weggelassen wurde, aber ich würde lieber eine Art Kompilierzeit-Feedback bekommen.
Alles, was ich hinzufügen zu tun hatte: import cats.implicits._ und das hat super funktioniert. –