关于上下文边界与隐式参数列表在路径依赖类型中行为差异的疑问
为什么上下文边界和隐式参数的返回类型表现不同?
这问题其实不是上下文边界和隐式参数本身的差异——上下文边界T: Foo本质就是(implicit ev: Foo[T])的语法糖,二者是等价的,真正导致类型差异的是你在summon2里用了implicitly[Foo[T]]这个操作,它丢失了隐式实例的具体类型信息。
咱们拆开来看两个方法的区别:
1. summon1的类型推断逻辑
def summon1[T](implicit f: Foo[T]) = f.value
当你调用summon1[Int]时,编译器会找到fooInt这个隐式实例,它的实际类型是Foo[Int] { type B = String }(而不仅仅是Foo[Int])。此时f就是这个具体的实例,f.value的类型是路径依赖类型f.B,也就是fooInt.B,编译器能精确推断出这就是String,所以返回类型是String。
2. summon2的类型推断逻辑
def summon2[T: Foo] = implicitly[Foo[T]].value
上下文边界T: Foo展开后其实是def summon2[T](implicit ev: Foo[T]) = implicitly[Foo[T]].value。这里的问题出在implicitly[Foo[T]]:
implicitly的定义是def implicitly[T](implicit ev: T): T = ev,它的作用是从隐式环境中提取一个T类型的实例,返回类型就是T。- 所以当你调用
implicitly[Foo[T]]时,它返回的实例类型被擦除成了Foo[Int](而不是fooInt的具体子类型),此时.value的类型就变成了类型投影Foo[Int]#B——这个类型代表所有Foo[Int]实例的B类型的公共超类型,而不是某个具体实例的路径依赖类型。
怎么让summon2返回具体类型?
如果你想让summon2和summon1表现一致,直接使用上下文边界对应的隐式参数即可(不需要用implicitly):
// 和summon1完全等价的写法 def summon2[T](implicit f: Foo[T]) = f.value // 或者显式指定返回类型为路径依赖类型 def summon2[T](implicit f: Foo[T]): f.B = f.value
这样写的话,summon2[Int]的返回类型会是fooInt.B也就是String,和summon1完全一致。
内容的提问来源于stack exchange,提问作者gogstad
相关产品推荐
相关产品推荐

