diff --git a/docs/src/main/tut/docs/effects/cache/README.md b/docs/src/main/tut/docs/effects/cache/README.md index 841e69e6..2eb2c3e0 100644 --- a/docs/src/main/tut/docs/effects/cache/README.md +++ b/docs/src/main/tut/docs/effects/cache/README.md @@ -8,30 +8,52 @@ permalink: /docs/effects/Cache/ The `Cache` effect algebra allows interacting with a _global_ key-value data store. It declares several abstract operations to read and write data into the store. -This algebra is parametrized on the types `K`, and `V`, for keys and values in the store, respectively. +This algebra is parametrized on the types `Key`, and `Val`, for keys and values in the store, respectively. -The algebra assumes no specific implementation or representation of the data store, for -that is given by each _interpreter or handler_ for the algebra. -For the same reason, it poses no type constraint on `K` or `V` with regards to ordering, hashing, or encoding. -Except, of course, that equality is defined for both types. +```Scala +class KeyValueProvider[Key, Val] { + @free sealed trait CacheM[F[_]] { + def get(key: Key): FreeS.Par[F, Option[Val]] + def put(key: Key, newVal: Val): FreeS.Par[F, Unit] + def del(key: Key): FreeS.Par[F, Unit] + def has(key: Key): FreeS.Par[F, Boolean] + def keys: FreeS.Par[F, List[Key]] + def clear: FreeS.Par[F, Unit] + } +} +``` -### Operations +To make the algebra parametric on the types of `Key` and `Value`, we wrap the declaration of the algebra inside a `KeyValueProvider`; but note that this class has no instance data. +The algebra assumes no specific implementation or representation of the data store, since that is a matter for each _handler_ of the algebra. +For the same reason, it poses no type constraint on `Key` or `Val` with regards to ordering, hashing, or encoding, but it is assumed that equality is defined for both types. -The set of abstract operations of the `Cache` algebra are specified as follows. +### Using the Cache Effect -```Scala -class KeyValueProvider[Key, Value] { - @free sealed trait CacheM[F[_]] { - def get(key: Key): FreeS.Par[F, Option[Val]] - def put(key: Key, newVal: Val): FreeS.Par[F, Unit] - def del(key: Key): FreeS.Par[F, Unit] - def has(key: Key): FreeS.Par[F, Boolean] - def keys: FreeS.Par[F, List[Key]] - def clear: FreeS.Par[F, Unit] - } +The following code snippet shows how to import and use the operations from the Cache algebra inside a program. + +```tut:book +import freestyle._ +import freestyle.implicits._ +import freestyle.cache._ +import cats.implicits._ + +val prov = new KeyValueProvider[Char, Int] + +import prov.CacheM +import prov.implicits._ + +def loadFrom[F[_]: prov.CacheM] = { + for { + a <- 1.pure[FreeS[F, ?]] + b <- CacheM[F].get('a') + c <- 1.pure[FreeS[F, ?]] + } yield a + b.getOrElse(0) + c } ``` -To make the algebra parametric on the types of `Key` and `Value`, we wrap the declaration of the algebra inside a `KeyValueProvider`; but note that this class has no instance data. + +### Operations + +The set of abstract operations of the `Cache` algebra are specified as follows. * `get(key: Key): M[Option[Val]]` issues a query to the data store on a given key. The result can be `None`, if the store has no mapping for that key, or `Some(v)` if the key is mapped to the value `v`. * `put(key: Key, v: Value)` issues a command to diff --git a/docs/src/main/tut/docs/integrations/akkahttp/README.md b/docs/src/main/tut/docs/integrations/akkahttp/README.md index 523859af..bda6a8ea 100644 --- a/docs/src/main/tut/docs/integrations/akkahttp/README.md +++ b/docs/src/main/tut/docs/integrations/akkahttp/README.md @@ -24,22 +24,28 @@ the [Akka HTTP docs](http://doc.akka.io/docs/akka-http/10.0.5/java/http/introduc Thus, the `freestyle` integration for Akka HTTP provides an extension of that _magnet_ pattern, which allow us to generate response [marshallers](http://doc.akka.io/docs/akka-http/current/scala/http/common/marshalling.html). -To be precise, what the integration gives is an `implicit` method that may generate an object of type - -```Scala -marsh: ToEntityMarshaller[ FreeS[ F, A ] ] -``` -for some types parameters `F[_]` , which is the target of the algebra, and for some base result type `A`. +To be precise, what the integration gives is a couple of `implicit` methods to generate an instance of + `ToEntityMarshaller[ FreeS[ F, A ] ] `, or `ToEntityMarshaller[ FreeS.Par[F, A]]`, where +the parameter `F[_]` is the target of the algebra, `A` is the type of the base result. Note that the method has to be parametrised both on the `F` and on `A`, to make it as generic as possible. -This is the method we have: - -```Scala - implicit def seqToEntityMarshaller[F[_], G[_], A]( - implicit NT: F ~> G, - MonG: Monad[G], - gem: ToEntityMarshaller[G[A]] - ): ToEntityMarshaller[FreeS[F, A]] = - gem.compose((fs: FreeS[F, A]) => fs.exec[G]) + +```tut:book +import freestyle._ +import freestyle.implicits._ +import cats.{ ~>, Monad } +import _root_.akka.http.scaladsl.marshalling.ToEntityMarshaller + +implicit def seqToEntityMarshaller[F[_], G[_], A]( + implicit NT: F ~> G, + MonG: Monad[G], + gem: ToEntityMarshaller[G[A]]): ToEntityMarshaller[FreeS[F, A]] = + gem.compose((fs: FreeS[F, A]) => fs.exec[G]) + +implicit def parToEntityMarshaller[F[_], G[_], A]( + implicit NT: F ~> G, + MonG: Monad[G], + gem: ToEntityMarshaller[G[A]]): ToEntityMarshaller[FreeS.Par[F, A]] = + gem.compose((fp: FreeS.Par[F, A]) => FreeS.liftPar(fp).exec[G]) ``` To build such an object `marsh`, our method needs to find in scope : @@ -66,7 +72,7 @@ First, we (1) define a domain class `User`, (2) write a `@free` algebra that returns values of a type `FreeS[F, User]`, and then (3) define an interpreter for that algebra to a type for which a `Marshaller` _magnet_ exists. To keep things simple, we just interpret to `Id`. -```Scala +```tut:book import freestyle._ import freestyle.implicits._ @@ -83,8 +89,9 @@ val app = UserApp.to[UserApp.Op] To use this `@free` algebra in a route, we need (1) an `EntityMarshaller` for our domain object, and (2) an interpreter of the algebra to a suitable domain, which for this example will be `Id`. -```Scala -import _root_.akka.http.scaladsl.marshalling.ToEntityMarshaller +```tut:book +import _root_.akka.http.scaladsl.marshalling.{ Marshaller, ToEntityMarshaller } +import cats.Id implicit val userMarshaller: ToEntityMarshaller[User] = Marshaller.StringMarshaller.compose((user: User) => s"User(${user.name})") @@ -97,7 +104,7 @@ implicit val handler: UserApp.Handler[Id] = new UserApp.Handler[Id] { With these in scope, one can now write the route by using the marshaller generators from Akka HTTP. -```Scala +```tut:book import _root_.akka.http.scaladsl.server.Directives._ import _root_.akka.http.scaladsl.server.Route import _root_.akka.http.scaladsl.marshalling.{Marshaller, ToEntityMarshaller} diff --git a/freestyle-cache/shared/src/test/scala/CacheTests.scala b/freestyle-cache/shared/src/test/scala/CacheTests.scala index 766c7599..652683b1 100644 --- a/freestyle-cache/shared/src/test/scala/CacheTests.scala +++ b/freestyle-cache/shared/src/test/scala/CacheTests.scala @@ -18,10 +18,9 @@ package freestyle.cache import cats.{Applicative, Id, ~>} import cats.syntax.cartesian._ -import cats.instances.option._ import freestyle._ import freestyle.implicits._ -import org.scalatest._ +import org.scalatest.{ Matchers, WordSpec } class CacheTests extends WordSpec with Matchers with CacheTestContext { @@ -30,6 +29,19 @@ class CacheTests extends WordSpec with Matchers with CacheTestContext { import provider.implicits._ import CacheM._ + "CacheM algebra" should { + + "allow a CacheM operation to interleaved inside a program monadic flow" in { + def prog[F[_]: CacheM]: FreeS[F, Int] = + for { + a <- FreeS.pure(1) + b <- CacheM[F].get("a") + c <- FreeS.pure(1) + } yield a + b.getOrElse(0) + c + prog[CacheM.Op].exec[Id] shouldBe 2 + } + } + "GET" should { "result in None if the datastore is empty" in { def program[F[_] : CacheM]: FreeS[F, Option[Int]] =