2011-03-03 162 views
2

假设您想要将一些方法添加到所有智能类型。这可以是这样的:在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],而不是更具体的类型。虽然构建的集合具有正确的(实际)类型。

回答

2

不幸的是,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遍这个第一关
  • 第二检查是RangeIterable[P]为一个子类一些P
  • 它与P推断为Int
  • 编译器是幸福的,所有的推论,边界检查等已成功
+0

它不应该为'String'工作(因为它是唯一可见的是'Seq'),但它应该'Map','范围“和其他可怜的生物。如果这是一个问题,为什么第二个版本不起作用?我怎样才能定义第二个隐式转换,它捕获所有不带类型参数的'Iterable'的子类型? – Raphael 2011-03-03 18:04:19

+0

@update:我明白了。然而,我不清楚为什么推理者不考虑超类型。由于你不能用不同的参数(?)扩展相同的特征,这应该产生明确的结果。至于我更简单的版本,它创建了正确的(动态)类型的集合,但只能静态地输入“Iterable”。那就是你得到'CanBuildFrom'的“底层”优势,但不是打字优势。 – Raphael 2011-03-04 11:13:41

+0

我已经扩展了我的答案,以更详细地解释。 – 2011-03-04 11:36:27