我需要获得作为参数给出的两种类型的最小上限。这样的事情:
class Holder[T] {
type Shrink[S] = ???
}
其中???
生成一些类型表达式,为T
和S
类型生成最小上限。看似LUB_Magic[T,S]
的东西。
我尝试过很多东西,但无法找到任何表达方式。
例如,如果预先定义类型A
和B
final case class Solve() {
val query = (x : Any) => x match {
case a : A => a
case b : B => b
}
type Q = query.type
}
将根据最小上限生成类型。
但是如果我尝试像class Solve[A,B]()
那样对它进行参数化,那么类型将是Any
而不是最小上限。 gist solution
是否有可能在类型级别获得最小上限?
答案 0 :(得分:1)
如果您的主要目标是在query
中使用正确的结果类型,那么您只需将A <:< C
和B <:< C
的隐式证据添加到query
的调用中即可。我已将这两种类型的证据分组到一个名为Lub
的类中:
class Lub[A, B, C](val ev1: A <:< C, val ev2: B <:< C)
object Lub {
implicit def lub[C, A <: C, B <: C]: Lub[A, B, C] =
new Lub[A, B, C](implicitly, implicitly)
}
现在您可以使用它来确保query
返回正确的函数类型:
import scala.reflect.ClassTag
case class Solve[A: ClassTag, B: ClassTag]() {
def query[C](implicit lub: Lub[A, B, C]): Any => C = {
(x: Any) => x match {
case a : A => lub.ev1(a)
case b : B => lub.ev2(b)
}
}
}
val s1 = Solve[Int, Float]
val q1: Any => AnyVal = s1.query // typechecks
如果您坚持q
为val
且最低上限为Solve
的类型成员而不是在呼叫网站上推断它,您可以执行以下操作:
abstract class Solve2[A: ClassTag, B: ClassTag] {
type C
def query: Any => C
}
object Solve2 {
final class Solve2Builder[A: ClassTag, B: ClassTag] {
def build[X](implicit lub: Lub[A, B, X])
: Solve2[A, B] { type C = X } = new Solve2[A, B] {
type C = X
val query: Any => C = _ match {
case a: A => lub.ev1(a)
case b: B => lub.ev2(b)
}
}
}
def apply[A: ClassTag, B: ClassTag]: Solve2Builder[A, B] = new Solve2Builder[A, B]
}
现在Solve2[A, B]{ type C = ... }
是使用随播对象创建的构建器构建的,A <:< C
和B <:< C
的隐式证据只能提供一次:
val s2 = Solve2[Int, Float].build
val q2: Any => AnyVal = s2.query
请注意,使用ClassTag
的所有这些操作只会带您到目前为止:您将能够区分Int
和Float
,但您无法区分List[Int]
和List[Float]
。在进一步调查之前,您可以仔细查看shapeless提供的内容。