Generics 在Scala中隐式转换泛型和非泛型子类型
假设您想向所有Iterables添加一些方法。可以这样看:Generics 在Scala中隐式转换泛型和非泛型子类型,generics,scala,implicit-conversion,Generics,Scala,Implicit Conversion,假设您想向所有Iterables添加一些方法。可以这样看: import collection.generic.CanBuildFrom class Foo[P, S[X] <: Iterable[X]](val s : S[P]) { def bar(j : P)(implicit bf : CanBuildFrom[S[P],P,S[P]]) : S[P] = { val builder = bf(s) builder ++= s builder += j
import collection.generic.CanBuildFrom
class Foo[P, S[X] <: Iterable[X]](val s : S[P]) {
def bar(j : P)(implicit bf : CanBuildFrom[S[P],P,S[P]]) : S[P] = {
val builder = bf(s)
builder ++= s
builder += j
builder.result
}
def oneBar(j : P)(implicit bf : CanBuildFrom[S[P],P,S[P]]) : P = bar(j).head
}
implicit def iter2foo[P, S[X] <: Iterable[X]](s : S[P]) = new Foo[P,S](s)
编译和执行顺畅。但是,
println((1 to 4) bar 5)
原因
error: value bar is not a member of scala.collection.immutable.Range.Inclusive
with scala.collection.immutable.Range.ByOne
我想这可能是因为隐式转换要求(?)参数的类型有一个类型参数(而Range
没有)。但是
按预期工作(在REPL上)。还是这样?这个解决方案有缺点吗
编辑2:缺点是
条形图的静态结果类型将仅为Iterable[p]
,而不是更具体的类型。不过,构造的集合具有正确的(实际)类型。遗憾的是,Range
扩展Iterable[Int]
并不重要,这确实是类型参数的算术问题。这也是一个深层次的问题,即使核心库在某些地方也会受到影响(只需查看清单中的注释即可)
如果您想使用映射、字符串等,就好像它们是Iterable
一样,您也会遇到它
我找到的唯一解决方案是定义多个到pimp类型的隐式转换
更新
这里的问题是从提供的参数推断类型参数p
,该参数似乎没有类型参数。实际上,您正在尝试对类型构造函数执行提取器对常规构造函数执行的操作,而多态性正在成为阻碍
您编辑的示例之所以有效,是因为不需要这个特定的推断,问题是您现在只能返回一个Iterable
,因此失去了CanBuildFrom
如果这不是一个问题,那么这是一个更简单的解决方案,所以继续吧
否则,对于要拉皮条的类型的每个可能的arity,您将需要不同的隐式
更新2
当试图确定范围
是否为有效参数时,请考虑编译器可能如何处理不同的表达式:
以1为例:
implicit def iter2foo[P, S[X] <: Iterable[X]](s : S[P]) = new Foo[P,S](s)
- 同样的问题,
S
仍然是参数不匹配的类型
第三个例子:
implicit def iter2foo[P](s : Iterable[P]) = new Foo[P,Iterable](s)
- 提供了参数后,
Iterable[P]
是一种简单的类型*
Range
通过第一关
- 第二项检查是,对于某些
P
- 是的,
P
推断为Int
- 编译器很高兴所有的推理、边界检查等都成功了
它不适用于字符串
(因为它只能作为序列
查看),但它应该适用于地图
、范围
和其他可怜的生物。如果这是一个算术问题,为什么第二个版本不起作用?我如何定义第二个隐式转换来捕获没有类型参数的Iterable
的所有子类型?@update:我明白了。然而,我不清楚为什么推理者不考虑超类型。由于不能用不同的参数(?)扩展同一特性,因此应该会产生明确的结果。至于我的简单版本,它创建了正确的(动态)集合类型,但只能静态地键入Iterable
。也就是说,您获得了CanBuildFrom
的“幕后”优势,但没有打字优势。我已经扩展了我的答案,以更详细地解释。感谢您的详细阐述,但我也理解了这一点。我的观点是:在第1步(甚至第2步)中,为什么编译器在无法匹配后不进行Range
的超类型线性化,并重复您提到的步骤?当然,这会增加编译器的运行时间,但这是可能的,对吗?奇怪的是,Range
的每个实例都是一个Iterable[Int]
,但在这一点上并没有这样对待,好像推理者忘记了这个事实。@Raphael-完全有可能是这样做的,但目前不是这样,因此在测试种类相等性时会出现失败。请放心,核心Scala开发团队已经知道了这个问题,尽管我不知道什么时候会得到解决,但修复起来比看起来更难!
implicit def iter2foo[P](s : Iterable[P]) = new Foo[P,Iterable](s)
implicit def iter2foo[P, S[X] <: Iterable[X]](s : S[P]) = new Foo[P,S](s)
implicit def iter2foo[P, S <: Iterable[P]](s : S) = new Foo[P,Iterable](s)
implicit def iter2foo[P](s : Iterable[P]) = new Foo[P,Iterable](s)