数据集过滤器:eta扩展不会自动完成

时间:2017-08-09 13:23:15

标签: scala apache-spark

如果我有一个简单的Int Scala集合,我定义了一个简单的方法 isPositive,如果值大于0则返回true,那么我可以将方法传递给集合的filter方法,如下例所示

def isPositive(i: Int): Boolean = i > 0

val aList = List(-3, -2, -1, 1, 2, 3)
val newList = aList.filter(isPositive)

> newList: List[Int] = List(1, 2, 3)

据我所知,编译器能够通过eta扩展自动将方法转换为函数实例,然后将此函数作为参数传递。

但是,如果我使用Spark数据集做同样的事情:

val aDataset = aList.toDS
val newDataset = aDataset.filter(isPositive)

> error

它失败了,方法"众所周知的"缺少参数;错误。为了使它工作,我必须使用" _"显式地将方法转换为函数:

val newDataset = aDataset.filter(isPositive _)

> newDataset: org.apache.spark.sql.Dataset[Int] = [value: int]

虽然使用map但它可以按预期工作:

val newDataset = aDataset.map(isPositive)

> newDataset: org.apache.spark.sql.Dataset[Boolean] = [value: boolean]

调查签名,我发现数据集过滤器的签名与List的过滤器非常相似:

// Dataset:
def filter(func: T => Boolean): Dataset[T]

// List (Defined in TraversableLike):
def filter(p: A => Boolean): Repr

那么,为什么编译器没有为数据集的过滤操作进行eta扩展呢?

1 个答案:

答案 0 :(得分:4)

这是由于重载方法和ETA扩展的性质。 Eta-expansion between methods and functions with overloaded methods in Scala解释了为什么会失败。

它的要点如下(强调我的):

  

超载时,适用性受到损害,因为没有   预期类型(6.26.3,臭名昭着)。当没有超载时,6.26.2   适用(eta扩展),因为参数的类型决定了   预期的类型。当重载时,arg是专门输入的   没有预期的类型,因此6.26.2不适用;因此都没有   d的重载变体被认为是适用的。

.....

  

超载分辨率的候选人已经过“形状”预先筛选。该   形状测试包含了从未使用过eta-expansion的直觉   因为args的类型没有预期的类型。这个例子说明了   eta扩展即使是“唯一的方式”,也不会被使用   表达式以检查。“

正如@DanielDePaula指出的那样,我们在DataSet.map中看不到此效果的原因是因为重载方法实际上需要额外的Encoder[U]参数:

def map[U : Encoder](func: T => U): Dataset[U] = withTypedPlan {
  MapElements[T, U](func, logicalPlan)
}

def map[U](func: MapFunction[T, U], encoder: Encoder[U]): Dataset[U] = {
  implicit val uEnc = encoder
  withTypedPlan(MapElements[T, U](func, logicalPlan))
}