首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Scala高类协变

Scala高类协变
EN

Stack Overflow用户
提问于 2015-10-24 10:54:08
回答 2查看 303关注 0票数 4

我试图抽象一些库API,它可以返回任何类型的AOption[A]Seq[A]

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

代码语言:javascript
复制
  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]

当我使用它时:

代码语言:javascript
复制
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]
}

我理解为什么这在F[_]不是covariant Seq[_]或类似的子类中是行不通的。但是,我不知道如何在仍然能够使用Const[A]的情况下工作。我没希望了吗?请帮帮忙。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2015-10-24 11:20:05

对于这种类型的多态性,您可以使用类型类型概念。

考虑

代码语言:javascript
复制
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]

我们可以定义这类类型:

代码语言:javascript
复制
trait HandleRequest[R <: Request[F, A], F[_], A] {
  def apply(request: R, client: Client): F[A]
}

并为所需的情况实例化它:

代码语言:javascript
复制
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
}

现在,您可以将您的一般功能定义为:

代码语言:javascript
复制
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)
}

例如,如果您喜欢泛化您的请求类型:

代码语言:javascript
复制
case class GetOne[X](id: Int) extends Request[Const, X]
case class GetMany[X]() extends Request[Seq, X]

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

代码语言:javascript
复制
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
EN

Stack Overflow用户

发布于 2015-12-08 09:33:57

回到这几个月后,我已经意识到,我可以使用path-dependent type实现同样的结果,同时更简洁,不需要类型类型模式。

代码语言:javascript
复制
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)
}

然后我们就可以在没有类型参数爆炸的情况下调用它:

代码语言:javascript
复制
def get[R <: Request](request: R): request.FA = request.query(client)
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/33317371

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档