如果我有一个简单的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扩展呢?
答案 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))
}