我正在尝试编写一些库函数来增强基本集合。大部分都进展顺利,但我遇到了这个问题。
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不使用一个参数来解析另一个参数,但单独的参数列表可以解决问题。然而,在我的情况下,这并不容易,因为类型信息可以在隐式参数中找到。
有没有解决方法,以便每次调用方法时都不必指定类型?
更新:根据Owen的建议,我最终创建了一个特定于可遍历对的丰富类:
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)
答案 0 :(得分: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看到隐含的 first 。好, 我们可以这样做的一种方法是添加一个额外的间接层:
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)