我试图让编译器基于已知的其他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]
}
正如您在此处所见,我希望能够加入两个读取查询(组合的详细信息无关紧要)。生成的新查询必须是ReturnsOne
或ReturnsMany
结果的查询。解决方案规则也非常简单:只有当两个查询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...
。
所以:
IS_ROLEMEMBER
我怎么能希望实现这个目标?
答案 0 :(得分:3)
对于这3个特征被密封的情况,你就是这样做的。如果它们可以扩展,它可能会变得更加复杂。
归结为以下几点。您有一个特征Fancy
,其中包含两种输入类型A
和B
,以及一种输出类型Out
。您可以定义隐式实例,以便在A
和B
都是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可能是了解此类模式的更好起点。