Scala更高级的类型和协变

Dan*_*hin 4 scala covariance higher-kinded-types

我正在尝试抽象一些可以返回任何类型的库API A,Option[A]或者Seq[A].

到目前为止,我有这样的事情:

  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]
Run Code Online (Sandbox Code Playgroud)

然后当我使用它时:

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]
}
Run Code Online (Sandbox Code Playgroud)

我理解为什么这不会起作用,因为F[_]它不是那种类的子类covariant Seq[_].但我不知道如何在能够使用的同时解决这个问题Const[A].我绝望了吗?请帮忙.

Odo*_*ois 7

对于这种类型的多态,您可以使用类型类概念
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]
Run Code Online (Sandbox Code Playgroud)

我们可以定义这样的类型类:

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

并针对所需案例进行实例化:

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
}
Run Code Online (Sandbox Code Playgroud)

现在您可以按如下方式定义常规函数:

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)
}
Run Code Online (Sandbox Code Playgroud)

如果您想要概括您的请求类型,例如:

case class GetOne[X](id: Int) extends Request[Const, X]
case class GetMany[X]() extends Request[Seq, X]
Run Code Online (Sandbox Code Playgroud)

您可以将实例重新定义为:

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
} 
Run Code Online (Sandbox Code Playgroud)