类型推理在泛型scala函数中失败



考虑一个对集合distinctBy进行操作的简单函数,该函数与distinct一样删除"重复项"(不是必需的实际重复项):

import scala.collection.TraversableLike
import scala.collection.generic.CanBuildFrom
import scala.collection.mutable.{Set=>MSet}
def distinctBy[T,R,Coll]
  (xs: Coll)
  (f: T => R)
  (implicit ev: Coll <:< TraversableLike[T,Coll], 
   cbf: CanBuildFrom[Coll,T,Coll]): Coll = {
  val builder = cbf(xs)
  builder.sizeHint(xs.size)
  val seen = MSet.empty[R]
  xs.foreach { elem =>
    if(!seen(f(elem))){
      builder += elem
    }
  }
  builder.result()
}

现在考虑一个类来使用它:

case class X(i: Int, j: Int)

使用这个功能天真地失败了:

scala> distinctBy(Vector(X(1,2),X(3,2),X(1,1),X(2,2)))(_.i)
<console>:14: error: missing parameter type for expanded function ((x$1) => x$1.i)
              distinctBy(Vector(X(1,2),X(3,2),X(1,1),X(2,2)))(_.i)
                                                          ^
<console>:14: error: Cannot construct a collection of type scala.collection.immutable.Vector[X] with elements of type Any based on a collection of type scala.collection.immutable.Vector[X].
              distinctBy(Vector(X(1,2),X(3,2),X(1,1),X(2,2)))(_.i)
                                                             ^

但是,如果我帮助类型推理器,这是有效的:

scala> distinctBy(Vector(X(1,2),X(3,2),X(1,1),X(2,2)))((x:X) => x.i)
res1: scala.collection.immutable.Vector[X] = Vector(X(1,2), X(3,2), X(1,1), X(2,2))
scala> distinctBy[X,Int,Vector[X]](Vector(X(1,2),X(3,2),X(1,1),X(2,2)))(_.i)
res2: scala.collection.immutable.Vector[X] = Vector(X(1,2), X(3,2), X(1,1), X(2,2))

据我所知,由于函数是在第二个参数列表中给出的,类型推理器应该已经发现它是从X到某个东西的函数。并且由于X具有类型为Int的成员i,所以第一次尝试应该都是可以的。那么,我在这里错过了什么?

这个简化版本对我来说很好:

object A {
  def f1[T, R](l: List[T])(f: T=>R) = None
  case class X(i: Int, j: Int)
  f1(List(X(1,1),X(2,1)))(_.i)
}

正如您所看到的,第一个参数列表中的集合具有T类型,该类型允许第二个参数列表的scala推理类型。

因此,在您的示例中,您需要在Col和T之间建立某种依赖关系。不确定第三个隐含参数列表在这里是否有帮助。

UPD。看起来很奇怪,但似乎有效:

object A {
  def f1[T, R, Col[Z]](l: Col[T])(f: T => R) = None
  case class X(i: Int, j: Int)
  f1(List(X(1,1),X(2,1)))(_.i)
}

UPD2。改写问题中的样本。

  import scala.collection.TraversableLike
  import scala.collection.generic.CanBuildFrom
  import scala.collection.mutable.{Set=>MSet}
  def distinctBy[T,R,Coll[Z]]
  (xs: Coll[T])
  (f: T => R)
  (implicit ev: Coll[T] <:< TraversableLike[T,Coll[T]],
   cbf: CanBuildFrom[Coll[T],T,Coll[T]]): Coll[T] = {
    val builder = cbf(xs)
    builder.sizeHint(xs.size)
    val seen = MSet.empty[R]
    xs.foreach { elem =>
      if(!seen(f(elem))){
        builder += elem
        seen.add(f(elem))
      }
    }
    builder.result()
  }
  case class X(i: Int, j: Int)
  distinctBy(Vector(X(1,2),X(1,2),X(3,2),X(1,1),X(2,2)))(_.i)
  distinctBy(Map("1" -> X(1,2), "2" -> X(1,2), "3" -> X(3,2)))(_._2.i)

最新更新