Creating an implicit function that wraps map() in

2019-07-24 18:10发布

问题:

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.

回答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)
  }
}