I'm trying to abstract over some library API that can return any of type A
, Option[A]
or Seq[A]
.
So far, I have something like this:
type Const[T] = T
sealed abstract class Request[F[_], A]
case class GetOne(id: Int) extends Request[Const, Int]
case class GetMany() extends Request[Seq, String]
And then when I use it:
def get[F[_], A](request: Request[F, A]): F[A] = request match {
case GetOne(id) => client.getOne[F[A]](id)
case GetMany() => client.getMany[A]() // error: Seq[A] does not conform to F[A]
}
I understand why this wouldn't work in that F[_]
is not subclass of covariant
Seq[_]
or something like that. But I'm not sure how I can work around while still being able to use Const[A]
. Am I hopeless? Please help.
For such type of polymorphism you could use typeclass concept
Considering
trait Client {
def getOne[X]: X
def getMany[X]: Seq[X]
}
type Const[T] = T
sealed abstract class Request[F[_], A]
case class GetOne(id: Int) extends Request[Const, Int]
case class GetMany() extends Request[Seq, String]
We could define such typeclass:
trait HandleRequest[R <: Request[F, A], F[_], A] {
def apply(request: R, client: Client): F[A]
}
And instantiate it for desired cases:
implicit object handleGetOne extends HandleRequest[GetOne, Const, Int] {
def apply(request: GetOne, client: Client): Int = client.getOne
}
implicit object handleGetMany extends HandleRequest[GetMany, Seq, String] {
def apply(request: GetMany, client: Client): Seq[String] = client.getMany
}
Now you could define your general function as follows:
implicit class ClientOps(val client: Client) {
def get[R <: Request[F, A], F[_], A](request: R)(implicit handle: HandleRequest[R, F, A]): F[A] =
handle(request, client)
}
If you ever like to generalize your request types, for instance:
case class GetOne[X](id: Int) extends Request[Const, X]
case class GetMany[X]() extends Request[Seq, X]
You could redefine your instances as:
implicit def handleGetOne[X] = new HandleRequest[GetOne[X], Const, X] {
def apply(request: GetOne[X], client: Client): X = client.getOne
}
implicit def handleGetMany[X] = new HandleRequest[GetMany[X], Seq, X] {
def apply(request: GetMany[X], client: Client): Seq[X] = client.getMany
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With