2012-04-01 65 views
0

缺少一个更好的示例,假设我有一个定义了容器类型的单一类型参数。假设这个容器包装了一个相同类型的List。我想在我的新容器上定义一个方法,以便每当执行操作时它将调用委托给嵌入列表,但它返回我的容器类型(可能是一个不同的类型参数)。为了实现这一点,我将使用scala集合中的隐式构建器模式。这里的基本结构:当选择隐式用于CanBuildFrom函数时,优先级的规则是什么

class Foo[A](val data: List[A]) { 
    def foo[C, That](pf: PartialFunction[A, C])(
    implicit bf: CanBuildFrom[Foo[_], C, That] 
): That = { 
    bf(new Foo(data.collect(pf))).result 
    } 
} 

object Foo { 
    def newBuilder[A]: Builder[A, Foo[A]] = 
    new ArrayBuffer[A] mapResult { r => new Foo(r.toList) } 

    implicit def canBuildFrom[A]: CanBuildFrom[Foo[_], A, Foo[A]] = 
    new CanBuildFrom[Foo[_], A, Foo[A]] { 
     def apply(from: Foo[_]) = newBuilder 
     def apply() = newBuilder 
    } 
} 

所以这个工程,我会期望返回一个Foo [字符串]当我的PF从int到字符串转换:在具有

scala> val f = new Foo(List(1,2,3)) 
f: Foo[Int] = [email protected] 

scala> f.foo { case x => x.toString } 
res318: Foo[java.lang.String] = [email protected] 

虽然前面的例子是基于CanBuildFrom采用Foo [_]的“from”类型,“A”的元素类型,并将其转换为“Foo [A]”的“to”类型。我想要做的是从'[']类型的List [_],一个元素类型'A',并转换为'到'类型'Foo [A]'。沿着这些路线的东西:

class Foo[A](val data: List[A]) { 
    def foo[C, That](pf: PartialFunction[A, C])(
    implicit bf: CanBuildFrom[List[_], C, That] 
): That = { 
    data.collect(pf)(bf) 
    } 
} 

object Foo { 
    def newBuilder[A]: Builder[A, Foo[A]] = 
    new ArrayBuffer[A] mapResult { r => new Foo(r.toList) } 

    implicit def canBuildFrom[A]: CanBuildFrom[List[_], A, Foo[A]] = 
    new CanBuildFrom[List[_], A, Foo[A]] { 
     def apply(from: List[_]) = newBuilder 
     def apply() = newBuilder 
    } 
} 

在这里,我已经通过我的隐CanBuildFrom参数关闭的List类直接,以便它可以建立我的Foo类以存储结果。然而,当我运行相同的测试,来代替。得到一个Foo [String],我得到一个List [String]。换句话说,它不使用我的隐式,它使用的是通用版本。

scala> val f = new Foo(List(1,2,3)) 
f: Foo[Int] = [email protected] 

scala> f.foo { case x => x.toString } 
res319: List[java.lang.String] = List(1, 2, 3) 

所以,我的问题是为什么?我会想,如果我现在的类型是Foo,并且我将转换为Foo,并且在范围中与输入参数类型(本例中为List)匹配的隐式fn,那么这将是最佳匹配。我是做错了什么,或者出于安全原因,“收集”集合在选择要转换的集合方面具有最高优先权。我能做些什么来提高隐含的优先级?

回答

0

它使用第一个匹配。由于在CanBuildFrom [List [_],C,That]匹配的范围中已经有一个canBuildFrom,它使用这个。您可以通过键入看到:

implicitly[CanBuildFrom[List[_], _, _]] 
// => res3: scala.collection.generic.CanBuildFrom[List[_], _, Any] = sca[email protected]6a3a191e 

但是,您可以强制编译器来搜索一个通过指定的结果存储在变量的类型返回美孚:

val y: Foo[String] = x.foo { case x => x.toString } 
// => y: Foo[String] = [email protected] 
+0

感谢您的意见。是的,我在发布之前检查了实际上在范围内的隐式。事情是我不想指定一个类型,我想要类型推断来完成这项工作。我认为我的隐含应该优先,但我想这不会是这样,直到它进入方法foo之后。我能想到的唯一解决方案是用'Foo [C]'替换'That',所以'to'类型是明确的。这很有效,但问题是,如果我没有'Foo'的'C'类型的支持,我希望它使用更通用的类型(按照CanBuildFrom模式)。这是行不通的。 – Mike 2012-04-02 02:33:55