2017-01-16 70 views
3

我想让编译器根据已知的其他2个类型参数推断某种类型。这里的例子:使用implicits来推断未知类型

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时,加入的查询在所有其他情况下也是ReturnsOneReturnsMany

所以:

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... 

我怎么能希望实现这一目标?

+0

如果''''R'''的类型为'ReturnsOne''',那么我需要'''R2'''来知道查询返回的结果数量。如果R的类型为'''ReturnsMany''',那么你是对的,它不会产生任何影响,因为它将是''''ManyMany'''无论如何 – shayan

+0

为什么由'R2'确定的结果数量和不是'R'?这里'R'的一般用法是什么? –

+0

@YuvalItzchakov它由R3确定,R3需要R和R2来决定。我正在寻找这个隐含的参数,它使用R和R2来解析R3,使其更加具体 – shayan

回答

3

这就是你将如何做这种事情的情况下,这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可能是一个很好的起点,以了解更多关于这样的模式。

+0

漂亮。谢谢 – shayan