I have a repo service that uses Tagless final and it looks like this:
final class MyRepoImpl[M[_]: Async](transactor: DoobieTransactor[M])(implicit ec: Scheduler)
extends MyRepo[M] {
override def isKnownElement(modelName: String): M[Boolean] = ???
override def isUpdateRequired(modelName: String): M[Boolean] = ???
override def currentDatePostgres(): M[String] = ???
}
I then have a service that calls this Repo like this:
final class Myservice[M[_]] {
def myMethod(id: Int): M[SomeReturnType] = {
// How can I compose isKnownElement first and if the return is true, process the isUpdateRequired and based on the response, I would like to return SomeReturnType
}
}
All I want to do is to compose the two mehods in the Repo class in my service class. I tried doing flatMap, but I do not find that signature. My repo trait looks like this:
trait MyRepo[M[_]] {
override def isKnownElement(modelName: String): M[Boolean] = ???
override def isUpdateRequired(modelName: String): M[Boolean] = ???
override def currentDatePostgres(): M[String] = ???
}
I want to acheive the following:
If isKnownElement returns true, I want to check isUpdateRequired and if isUpdateRequired returns true, I need to return SomeResultType which contains a status field with status Rejected
If isKnownElement returns false, I would like to return SomeResultType with status Rejected
If isKnownElement returns true and isUpdateRequired returns false, I need to return SomeResultType with status Accepted
How can I elegantly map this elegantly?
If you need to ensure that isUpdateRequired
is only called if and only if isKnownElement
returns true
import cats.Monad
import cats.syntax.all.*
final class Myservice[M[_]: Monad](
repository: MyRepo[M]
) {
def myMethod(id: Int): M[SomeReturnType] =
repository.isKnownElement(modelName = ???).flatMap {
case true =>
repository.isUpdateRequired(modelName = ???).map {
case true =>
SomeReturnType(status = Rejected)
case false =>
SomeReturnType(status = Accepted)
}
case false =>
IO.pure(SomeReturnType(status = Rejected))
}
}
If you can just call both operations (either sequentially or in parallel)
import cats.Monad
import cats.syntax.all.*
final class Myservice[M[_]: Monad](
repository: MyRepo[M]
) {
def myMethod(id: Int): M[SomeReturnType] =
// You may use parMapN if you want to call both methods in parallel.
(
repository.isKnownElement(modelName = ???),
repository.isUpdateRequired(modelName = ???)
).mapN {
case (true, false) => SomeReturnType(status = Accepted)
case (true, true) => SomeReturnType(status = Rejected)
case (false, _) => SomeReturnType(status = Rejected)
}
}
You can also use for
, if / else
, and many other syntactical changes depending on your style.
But, the core idea remains, you just compose programs. Nothing fancy.
PS: This is a good example of Boolean blindless, you may use some ADTs instead of booleans so the pattern match version reads better.