Scala is a higher type and covariant

I am trying to abstract from some library API that can return any types A, Option[A]or Seq[A].

So far I have had 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 will not work, because F[_]it is not a subclass covariant Seq[_]or something like that. But I'm not sure how I can work, while retaining the ability to use Const[A]. Am I really hopeless? Please, help.

+4
source share
2 answers

For this type of polymorphism, you can use the concept of typeclass
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 a class like this:

trait HandleRequest[R <: Request[F, A], F[_], A] {
  def apply(request: R, client: Client): F[A]
}

:

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
}

:

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)
}

- , :

case class GetOne[X](id: Int) extends Request[Const, X]
case class GetMany[X]() extends Request[Seq, X]

:

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
} 
+7

, , path-dependent type , typeclass.

type Const[T] = T

sealed trait Request {
  type F[_]
  type A
  type FA = F[A]

  def query(client: Client): Future[FA]
}

case class GetOne(id: Int) extends Request {
  type F[x] = Const[x]
  type A = Int
  def query(client: Client): Future[Int] = client.getOne(id)
}

case class GetMany(id: Int) extends Request {
  type F[x] = Seq[x]
  type A = String
  def query(client: Client): Future[Seq[String]] = client.getMany(id)
}

:

def get[R <: Request](request: R): request.FA = request.query(client)
+1

Source: https://habr.com/ru/post/1613013/


All Articles