1

I am trying to implement an implicit function mapMetered that wraps map and functions exactly like it in terms of returning the correct type. I tried this:

implicit class MeteredGenTraversablePimp[T, C[T] <: GenTraversable[T]](trav: C[T]) {
  def foreachMetered(m: Meter)(f: T => Unit) =
    m.foreach(trav)(f)

  def mapMetered[B, That](m: Meter)(f: (T) => B)(
    implicit bf: CanBuildFrom[C[T], B, That]
  ): That = {
    m.start()
    try {
      trav.map { x =>
        val z = f(x)
        m.item_processed()
        z
      } (bf)
    } finally { m.finish() }
  }
}

But when I try this I get an error:

[info] Compiling 1 Scala source to /Users/benwing/devel/textgrounder/target/classes...
[error] /Users/benwing/devel/textgrounder/src/main/scala/opennlp/textgrounder/util/metering.scala:223: type mismatch;
[error]  found   : scala.collection.generic.CanBuildFrom[C[T],B,That]
[error]  required: scala.collection.generic.CanBuildFrom[scala.collection.GenTraversable[T],B,That]
[error]         } (bf)
[error]            ^
[error] one error found

There are similar Stack Overflow questions, including one from Daniel Sobral who suggests writing (trav: C[T] with GenTraversableLike[T]) but this doesn't fix the problem.

Urban Vagabond
  • 7,282
  • 3
  • 28
  • 31
  • Is what you're doing similar to the function of `scala.collection.breakOut` (described [here](http://stackoverflow.com/questions/1715681/scala-2-8-breakout))? – DaoWen Dec 18 '13 at 05:54
  • @DaoWen: No, I'm just trying to create an implicit function that behaves like `map` and returns the correct type. In my case, this is a metered version of `map` that counts how long it took to do the whole operation, how fast each iteration was executed, etc. – Urban Vagabond Dec 22 '13 at 21:32

1 Answers1

1

The Repr parameter in CanBuildFrom and in the *Like collection types is already there to represent the most precise type of the collection. The solution to your problem is to wrap a GenTraversableLike[A,Repr] instead of a C[T]. The compiler will infer the exact type as Repr, and everything will work flawlessly:

implicit class MeteredGenTraversablePimp[A, Repr](trav: GenTraversableLike[A,Repr]) {
  def foreachMetered(m: Meter)(f: A => Unit) = {
    m.start()
    try {
      trav.foreach{ e => f( e ); m.item_processed() }
    } finally {
      m.finish()
    }
  }

  def mapMetered[B, That](m: Meter)(f: A => B)(
    implicit bf: CanBuildFrom[Repr, B, That]
  ): That = {
    m.start()
    trav.map { x: A =>
      try {
        val z: B = f(x)
        m.item_processed()
        z
      } finally { m.finish() }
    }(bf)
  }
}
Régis Jean-Gilles
  • 32,541
  • 5
  • 83
  • 97
  • BTW I asked another question along the same lines that you might be able to answer; I'd be very grateful if you could: http://stackoverflow.com/questions/20734629/another-scala-canbuildfrom-issue-a-pimping-collection-operator-that-wraps-anoth – Urban Vagabond Dec 22 '13 at 22:17