假设您要为所有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
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(Seq(1,2,3,4) bar 5)
编译并顺利执行。但是,
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
没有)。但
implicit def iter2foo[P, S <: Iterable[P]](s : S) = new Foo[P,Iterable](s)
不会改变任何事情。请注意,Range
扩展了Iterable[Int]
。
我做错了什么?如何编写一个适用于Iterable
的所有子类型的隐式转换,是否通用?
编辑:我只是注意到更简单的
implicit def iter2foo[P](s : Iterable[P]) = new Foo[P,Iterable](s)
按预期工作(在REPL上)。或者是吗?这个解决方案有缺点吗?
编辑2:缺点是bar
的静态结果类型只是Iterable[P]
,而不是更具体的类型。但是,构造的集合具有正确的(实际)类型。
答案 0 :(得分:2)
可悲的是,Range
延伸Iterable[Int]
并不重要,这确实是类型参数类型的问题。它也是一个很深的版本,即使是核心库也会受到影响(只需查看Manifest
中的注释)
如果想要使用地图,字符串等,您也会遇到它,就好像它们是Iterable
一样。
我发现的唯一解决方案是为pimp类型定义多个隐式转换。
<强>更新强>
这里的问题是从提供的参数推断出类型参数P
,该参数似乎没有类型参数。你实际上是想为类型构造函数做一些提取器将为常规构造函数做的事情,并且多态性正在阻碍。
您编辑的示例有效,因为不需要进行此特定推理,现在您只能返回Iterable
,因此失去了CanBuildFrom
的大部分好处
如果这不是问题,那么这是一个更简单的解决方案,所以请滚动它。
否则,你需要对你想要皮条客的每种可能的arity采用不同的含义。
更新2
在尝试确定Range
是否为有效参数时,请考虑编译器如何处理不同的表达式:
拿1:
implicit def iter2foo[P, S[X] <: Iterable[X]](s : S[P]) = new Foo[P,S](s)
S
是一种类型较高的类型* => *
Range
是一种类型*
拿2:
implicit def iter2foo[P, S <: Iterable[P]](s : S) = new Foo[P,Iterable](s)
S
仍然是* => *
参数不匹配拿3:
implicit def iter2foo[P](s : Iterable[P]) = new Foo[P,Iterable](s)
Iterable[P]
是一种简单类型*
Range
通过了第一个障碍Range
是某些Iterable[P]
的{{1}}的子类P
推断为P