使用implicits推断未知类型

时间:2017-01-16 13:51:24

标签: scala implicit-conversion type-inference implicit

我试图让编译器基于已知的其他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...

所以:

IS_ROLEMEMBER

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

1 个答案:

答案 0 :(得分: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可能是了解此类模式的更好起点。