I’m trying to write some library functions to enhance the basic collections. Most of it has gone smoothly, but I’m having an issue with this one.
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)
Here’s what happens when I go to use it:
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)
^
So Scala is unable to determine the type of x.
This answer indicates Scala doesn’t use one parameter to resolve another, but that separate parameter lists can fix the problem. In my case, however, this isn’t so easy since the type information is found in the implicit parameters.
Is there a way around this so that I don’t have to specify the type every time I call the method?
Update: Based on Owen’s advice, I ended up creating a enriched class specific to a traversable of pairs:
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)
Yes, there is. Let me give a simpler example. I hope this will also work with
your more complicated use case.
say we have
Now this has exactly the problem you describe, since
gives “missing parameter type”.
So what we would like to do, is to move the implicit parameter “behind” the
explicitly supplied function, so that Scala sees the implicit first. Well,
one way we can do this is to add an extra layer of indirection:
This works, though the syntax is not so pretty. One way you might consider
prettying the syntax is to look at your current design and see if you can sneak
the implicit into any of the “earlier” stages. For example, since the
mapValuesStrictmethod is only meaningful once the implicit has beensupplied, you might make the implicit a property of the object instead of
passed to the method.
But if that is not convenient in your design, you could use an extra implicit
conversion to sneak it back. This is what we would like to do:
But unfortunately there is what I suspect is a bug in Scala that causes it to
search for an implicit value that is too polymorphic, causing it to complain:
This only happens when using implicit conversions, which is why I think it is a
bug. So, we can take it back even further: (and, requiring
-Xexperimentalfor dependent method types):
And now
works perfectly 😉
Update
In your particular case, I think your best bet is to work the implicit into
enhanceGenTraversable, though, alas, the same hack is required to work around the possible bug: