2012-02-01 62 views
4

我试图编写一些库函数来增强基本集合。它大部分进展顺利,但我遇到了这个问题。斯卡拉:错误:缺少参数类型

class EnhancedGenTraversableLike[A, Repr <: GenTraversable[A]](self: GenTraversableLike[A, Repr]) { 
    def mapValuesStrict[T, U, R, That](f: U => R)(implicit ev: A <:< (T, U), bf: CanBuildFrom[Repr, (T, R), That]) = { 
    val b = bf(self.asInstanceOf[Repr]) 
    b.sizeHint(self.size) 
    for ((k: T, v: U) <- self) b += k -> f(v) 
    b.result 
    } 
} 
implicit def enhanceGenTraversableLike[A, Repr <: GenTraversable[A]](self: GenTraversableLike[A, Repr]) = new EnhancedGenTraversableLike[A, Repr](self) 

这里是当我去使用它会发生什么:

scala> List((1,2),(2,3),(3,4),(2,5)).mapValuesStrict((_:Int).toString) 
res0: List[(Int, java.lang.String)] = List((1,2), (2,3), (3,4), (2,5)) 

scala> List((1,2),(2,3),(3,4),(2,5)).mapValuesStrict(x => x.toString) 
<console>:13: error: missing parameter type 
       List((1,2),(2,3),(3,4),(2,5)).mapValuesStrict(x => x.toString) 
                  ^

所以Scala是无法确定的x类型。

This answer指示Scala不使用一个参数来解析另一个参数,但是单独的参数列表可以解决该问题。然而,就我而言,这并不容易,因为类型信息可以在隐式参数中找到。

是否有解决方法,以便我不必每次调用方法时指定类型?


更新:根据欧文的建议,我结束了创建具体到对一笔画一个丰富的类:

class EnrichedPairGenTraversableLike[T, U, Repr <: GenTraversable[(T, U)]](self: GenTraversableLike[(T, U), Repr]) { 
    def mapValuesStrict[R, That](f: U => R)(implicit bf: CanBuildFrom[Repr, (T, R), That]) = { 
    val b = bf(self.asInstanceOf[Repr]) 
    b.sizeHint(self.size) 
    for ((k: T, v: U) <- self) b += k -> f(v) 
    b.result 
    } 
} 
implicit def enrichPairGenTraversableLike[T, U, Repr <: GenTraversable[(T, U)]](self: GenTraversableLike[(T, U), Repr]) = new EnrichedPairGenTraversableLike(self) 

回答

6

是的,有。让我举一个更简单的例子。我希望这也可以与 更复杂的用例一起使用。

说,我们有

trait Foo[A] 

class Bar { 
    def methWithImplicits[A,B](f: A => B)(implicit foo: Foo[A]) = null 
} 

implicit def fooInt: Foo[Int] = null 

现在这正好有你所描述的问题,因为

(new Bar).methWithImplicits(x => x) 

给 “缺少的参数类型”。

所以我们想要做的是将明确提供的函数的隐含参数“移动”到“后面”,以便Scala看到隐含的第一个。那么, 我们可以做到这一点的方法之一是增加额外的间接层:

class Bar { 
    def methWithImplicits2[A](implicit foo: Foo[A]) = new { 
     def apply[B](f: A => B) = null 
    } 
} 

(new Bar).methWithImplicits2.apply(x => x) 

这工作,虽然语法不那么漂亮。你可能会认为 的一种方法很漂亮的语法是看你目前的设计,看看你是否可以暗示 隐含在任何“早期”阶段。例如,由于 mapValuesStrict方法只有在提供隐式已提供 时才有意义,因此可能会将该对象的属性隐式传递给该方法,而不是 。

但是,如果在您的设计中不方便,您可以使用一个额外的隐含 转换来偷袭它。这是我们希望做什么:

implicit def addFoo[A](bar: Bar)(implicit foo: Foo[A]) = new { 
    def methWithImplicits3[B](f: A => B) = null 
} 

但可惜的是,我怀疑什么是Scala,导致它 搜索是太多态性的隐含价值,使其抱怨了一个错误:

could not find implicit value for parameter foo: test.Foo[A] 

这只发生在使用隐式转换时,这就是为什么我认为它是一个 的错误。因此,我们可以把它回更进一步:(和,要求-Xexperimental 供养方法类型):

trait FooWrapper { 
    type AA 
    val foo: Foo[AA] 
} 

implicit def wrapFoo[A](implicit theFoo: Foo[A]) = new FooWrapper { 
    type AA = A 
    val foo = theFoo 
} 

implicit def addFoo(bar: Bar)(implicit foo: FooWrapper) = new { 
    def methWithImplicits3[B](f: foo.AA => B) = null 
} 

现在

(new Bar).methWithImplicits3(x => x) 

作品完美;)


更新

你的具体情况,我认为最好的办法是隐式工作纳入enhanceGenTraversable,不过,唉,同样的黑客需要解决的可能的错误:

// Notice `ev` is now a field of the class 
class EnhancedGenTraversableLike[A, Repr <: GenTraversable[A], T, U] 
    (self: GenTraversableLike[A, Repr], ev: A <:< (T, U)) 
{ 
    def mapValuesStrict[R, That](f: U => R)(implicit bf: CanBuildFrom[Repr, (T, R), That]) = { 
     val b = bf(self.asInstanceOf[Repr]) 
     b.sizeHint(self.size) 
     for ((k: T, v: U) <- self) b += k -> f(v) 
     b.result 
    } 
} 

// The Hack 
trait WrappedPairBound[A] { 
    type TT 
    type UU 
    val bound: A <:< (TT, UU) 
} 

implicit def wrapPairBound[A,T,U](implicit ev: A <:< (T,U)) = new WrappedPairBound[A] { 
    type TT = T 
    type UU = U 
    val bound = ev 
} 

// Take the implicit here 
implicit def enhanceGenTraversableLike[A, Repr <: GenTraversable[A]] 
     (self: GenTraversableLike[A, Repr])(implicit ev: WrappedPairBound[A]) = 
    new EnhancedGenTraversableLike[A, Repr, ev.TT, ev.UU](self, ev.bound) 
+0

感谢伟大的答案!我最终将事情分成两个“增强”类,所以我可以有一个特定于'GenTraversableLike [(T,U),Repr]'的类。这显示了克服了这个问题,并且在概念上也很干净。 – dhg 2012-02-01 05:44:56