简体   繁体   English

通用Scala函数中类型推断失败

[英]type inference fails in a generic scala function

consider a simple function that operates on collections distinctBy , which, like distinct remove "duplicates" (which are not necessary actual duplicates): 考虑一个对集合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()
}

now consider a class to use it on: 现在考虑使用一个类:

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

using this function naively fails: 天真的使用此功能会失败:

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)
                                                             ^

but if I help the type inferencer, this works: 但是如果我帮助类型推断器,则可以:

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))

to my best understanding, since the function is given in a second argument list, the type inferencer should have picked up that it's a function from X to something. 据我所知,由于该函数是在第二个参数列表中给出的,因此类型推断器应该已经知道它是从X到某个对象的函数。 and since X has a member i of type Int , all should have been OK with the first try. 并且由于X的成员i的类型为Int ,因此第一次尝试都应该可以。 so, what am I missing here? 所以,我在这里想念什么?

This simplified version works fine for me: 这个简化的版本对我来说很好用:

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

As you can see collection in first parameter list has T type that allows scala inference type in second arguments list. 如您所见,第一个参数列表中的集合具有T类型,该类型允许第二个参数列表中的scala推断类型。

So you need build somehow dependencies between Col and T in your example. 因此,您需要在示例中在Col和T之间建立某种依赖关系。 Not sure if third implicits parameters list helps here. 不确定第三个隐式参数列表是否对您有所帮助。

UPD. UPD。 Looks weird but seems it works: 看起来很奇怪,但似乎可行:

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. UPD2。 Rewritten sample from question. 从问题重写示例。

  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)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM