I have a Service that looks like this:
The Tagless final trait:
trait ProvisioningAPIService[M[_]] {
def provisionAPI(request: Request): M[Response]
}
And somewhere in my implementation, I have the following:
class ProvisioningService extends ProvisioningAPIService[Task] {
def provisionAPI(request: Request): Task[Response] = Task {
Response("response from server")
}
}
This is fine, but I would like to still delay that and pass in the effect only when I instantiate a new version of my ProvisioningService. For example., I would like to have something like this:
class ProvisioningService[M[_]: Monad](implicit monad: Monad[M[_]) extends ProvisioningAPIService[M] {
def provisionAPI(request: Request): M[Response] = /* some wrapper that would be resolved at runtime */ {
Response("response from server")
}
}
And during runtime, I do the following:
val privisioingServiceAsTask = new ProvisioningService[Task]
So basically I do not provide a concrete implemenattion during compile time, but I create an instance of my ProvisioningService passing in an effect at Runtime. How can I do this?
CodePudding user response:
Moving the comment to an answer,
Saying class MyClass[A : Monad]
is the exact same thing is as saying class MyClass[A](implicit m: Monad[A])
so you don't need both. In this case you will actually want to be able to call it by name so you should prefer the latter.
You can read about context bounds for Scala 3 here and Scala 2 here
Now let's look at what Type Classes. Type classes essentially enable overloading per type basis in a nice way.
So when you say you have a Monad[A]
defined in the scope, it means that A
will have the 3 operations the Monad
trait contains defined. The details of the implementation are abstracted away so we don't have to worry about them. If you look at the page for Monad and by extension Applicative you will see that any instance of a Monad requires you to have an implementation of Pure
which wraps a value inside the type constructor. What this pure
does will be left to the implementation of the instance for a given effect. It can be Future.successful
for example.
So you can have something along the lines of
import cats._
import cats.effect.IO
case class Request(s: String)
case class Response(s: String)
trait ProvisioningAPIService[M[_]] {
def provisionAPI(request: Request): M[Response]
}
class ProvisioningService[M[_]](implicit monad: Monad[M]) extends ProvisioningAPIService[M] {
def provisionAPI(request: Request): M[Response] = monad.pure(Response("response from server"))
}
val privisioingServiceAsTask = new ProvisioningService[IO]
Hope this answers your question