在Scala中隐式转换泛型和非泛型子类型
问题描述:
假设您想要将一些方法添加到所有智能类型。这可以是这样的:在Scala中隐式转换泛型和非泛型子类型
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)
不会改变任何东西。请注意0延伸Iterable[Int]
。
我在做什么错?我怎样才能写出一个适用于所有Iterable
的子类型的隐式转换?
编辑:我只是注意到按预期(上REPL)更简单
implicit def iter2foo[P](s : Iterable[P]) = new Foo[P,Iterable](s)
作品。还是呢?这种解决方案有什么缺点吗?
编辑2:的缺点是的bar
静态结果类型只会Iterable[P]
,而不是更具体的类型。虽然构建的集合具有正确的(实际)类型。
答
不幸的是,Range
延伸Iterable[Int]
并不重要,这确实是类型参数的问题。这是一深一过,甚至核心库受到它的地方(只要看看评论中Manifest
)
你也会碰到它,如果想要使用地图,字符串,等等,仿佛它们是Iterable
。
我发现的唯一解决方案是定义多个隐式转换为pimp类型。
UPDATE
这里的问题是在从所提供的参数,它不会出现有型参数推断所述类型参数P
。你基本上正在尝试为类型构造函数做一些操作,而提取器会为常规构造函数做些什么,而多态性正在阻碍它。
编辑过的示例工作,因为不需要这种特殊的推断,美中不足的是,你现在只能返回Iterable
,因此失去很多的CanBuildFrom
的利益。如果这不是一个问题,那么它的一个更简单的解决方案,所以滚动它。
否则,您需要针对您想要皮条客的每种可能类型的不同含义。
更新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]
为一个子类一些P
- 它与
P
推断为Int
- 编译器是幸福的,所有的推论,边界检查等已成功
它不应该为'String'工作(因为它是唯一可见的是'Seq'),但它应该'Map','范围“和其他可怜的生物。如果这是一个问题,为什么第二个版本不起作用?我怎样才能定义第二个隐式转换,它捕获所有不带类型参数的'Iterable'的子类型? – Raphael 2011-03-03 18:04:19
@update:我明白了。然而,我不清楚为什么推理者不考虑超类型。由于你不能用不同的参数(?)扩展相同的特征,这应该产生明确的结果。至于我更简单的版本,它创建了正确的(动态)类型的集合,但只能静态地输入“Iterable”。那就是你得到'CanBuildFrom'的“底层”优势,但不是打字优势。 – Raphael 2011-03-04 11:13:41
我已经扩展了我的答案,以更详细地解释。 – 2011-03-04 11:36:27