使用隐式推断未知类型

Use implicits to infer an unknown type

我试图让编译器根据已知的其他 2 个类型参数推断某种类型。这是示例:

trait ReturnCount
trait ReturnsMany extends ReturnCount
trait ReturnsOne extends ReturnCount

class Query[R <: ReturnCount]{
    def join[R2 <: ReturnCount,R3 <: ReturnCount](q: Query[R2]): Query[R3]
}

正如您在这里看到的,我希望能够加入两个读取查询(组成的细节与其无关)。生成的新查询必须是 ReturnsOneReturnsMany 结果的查询。解析规则也很简单:只有当两个查询都 ReturnsOne 时,联合查询也 ReturnsOne 在所有其他情况下它 ReturnsMany.

所以:

val q1 = new Query[ReturnsOne]
val q2 = new Query[ReturnsMany]
val q3 = q1 join q2 //I don't want to have 
                    //to specify R3 because compiler should do it for me...

我希望如何实现这一目标?

这就是在这 3 个特性被密封的情况下你会如何做这样的事情。如果他们对扩展开放,它可能会变得更加复杂。

归结为以下几点。您有一个特征 Fancy,具有两种输入类型 AB,以及一种输出类型 Out。您定义隐式实例,以便在 AB 都为 ReturnsOne 的情况下,Out 将为 ReturnsOne。在所有其他情况下,您会退回到优先级较低的默认情况(因为否则您会收到歧义错误),其中 Out 始终是 ReturnsMany.

scala> :paste
// Entering paste mode (ctrl-D to finish)

sealed trait ReturnCount
sealed trait ReturnsMany extends ReturnCount
sealed trait ReturnsOne extends ReturnCount

sealed trait Fancy[A, B] {
  type Out <: ReturnCount
}

object Fancy extends LowerPriority {
  type Aux[A,B,Out1 <: ReturnCount] = Fancy[A,B] { type Out = Out1 }

  implicit def returnsOne: Fancy.Aux[ReturnsOne,ReturnsOne,ReturnsOne] = 
    new Fancy[ReturnsOne,ReturnsOne] { type Out = ReturnsOne }
}

trait LowerPriority {
  implicit def returnsMany[A,B]: Fancy.Aux[A,B,ReturnsMany] = 
    new Fancy[A,B] { type Out = ReturnsMany }
}

class Query[R <: ReturnCount] { 
  def join[R2 <: ReturnCount](q: Query[R2])(implicit fancy: Fancy[R,R2]): Query[fancy.Out] = ??? 
}


// Exiting paste mode, now interpreting.

defined trait ReturnCount
defined trait ReturnsMany
defined trait ReturnsOne
defined trait Fancy
defined object Fancy
defined trait LowerPriority
defined class Query

scala> :type new Query[ReturnsOne].join(new Query[ReturnsOne])
Query[ReturnsOne]

scala> :type new Query[ReturnsOne].join(new Query[ReturnsMany])
Query[ReturnsMany]

scala> :type new Query[ReturnsMany].join(new Query[ReturnsMany])
Query[ReturnsMany]

scala> :type new Query[ReturnsMany].join(new Query[ReturnsOne])
Query[ReturnsMany]

在实际情况下,您的 Fancy 可能还需要一个 join 方法,该方法将包含您的 Query#join 方法委托给的实际实现:

class Query[R <: ReturnCount] { 
  def join[R2 <: ReturnCount](q: Query[R2])(implicit fancy: Fancy[R,R2]): Query[fancy.Out] =
    fancy.join(this, q)
}

This blog 可能是了解更多此类模式的良好起点。