gpt4 book ai didi

Scala:groupBy(_.getClass) 的转换结果

转载 作者:行者123 更新时间:2023-12-05 04:07:05 25 4
gpt4 key购买 nike

在这个假设中,我有一个要执行的操作列表。如果可以将列表中的某些操作一起批处理(例如,从数据库中的同一个表中查找不同的行),则该列表中的某些操作将更加高效。

trait Result
trait BatchableOp[T <: BatchableOp[T]] {
def resolve(batch: Vector[T]): Vector[Result]
}

这里我们使用F-bounded Polymorphism允许操作的实现引用它自己的类型,这是非常方便的。

但是,这会在执行时带来问题:

def execute(operations: Vector[BatchableOp[_]]): Vector[Result] = {
def helper[T <: BatchableOp[T]](clazz: Class[T], batch: Vector[T]): Vector[Result] =
batch.head.resolve(batch)

operations
.groupBy(_.getClass)
.toVector
.flatMap { case (clazz, batch) => helper(clazz, batch)}
}

这会导致编译器错误,指出 inferred type arguments [BatchableOp[_]] do not conform to method helper's type parameter bounds [T <: BatchableOp[T]] .

Scala 编译器如何确信 group都是同一类型(它是 BatchableOp 的子类)吗?

最佳答案

我想系统地处理这个问题,以便在类似的情况下可以应用相同的解决策略。

首先,一个显而易见的评论:您想要使用向量。向量的内容可以是不同的类型。矢量的长度没有限制。向量条目类型的数量不受限制。因此,编译器无法在编译时证明一切:您将不得不在某些时候使用类似 asInstanceOf 的东西。


现在开始解决实际问题:

这里是在 2.12.4 下编译的:

import scala.language.existentials

trait Result

type BOX = BatchableOp[X] forSome { type X <: BatchableOp[X] }

trait BatchableOp[C <: BatchableOp[C]] {
def resolve(batch: Vector[C]): Vector[Result]

// not abstract, needed only once!
def collectSameClassInstances(batch: Vector[BOX]): Vector[C] = {
for (b <- batch if this.getClass.isAssignableFrom(b.getClass))
yield b.asInstanceOf[C]
}

// not abstract either, no additional hassle for subclasses!
def collectAndResolve(batch: Vector[BOX]): Vector[Result] =
resolve(collectSameClassInstances(batch))
}

def execute(operations: Vector[BOX]): Vector[Result] = {

operations
.groupBy(_.getClass)
.toVector
.flatMap{ case (_, batch) =>
batch.head.collectAndResolve(batch)
}
}

我在这里看到的主要问题是,在 Scala 中(与某些实验性依赖类型语言不同),没有简单的方法来“在类型存在的假设下”写下复杂的计算。因此,似乎很难/不可能改造

Vector[BatchOp[T] forSome T]

变成一个

Vector[BatchOp[T]] forSome T

这里,第一种类型表示:“它是一个 batchOps 向量,它们的类型是未知的,并且可以完全不同”,而第二种类型表示:“它是一个未知类型的 batchOps 向量T,但至少我们知道它们都是一样的”。

您想要的是类似于以下假设的语言结构:

val vec1: Vector[BatchOp[T] forSome T] = ???
val vec2: Vector[BatchOp[T]] forSome T =
assumingExistsSomeType[C <: BatchOp[C]] yield {
/* `C` now available inside this scope `S` */
vec1.map(_.asInstanceOf[C])
}

不幸的是,对于存在类型我们没有类似的东西,我们不能在某些范围 S 中引入辅助类型 C 这样当 C 被淘汰,我们只剩下一个存在主义的(至少我没有看到一个通用的方法来做到这一点)。

因此,这里唯一要回答的有趣问题是:

Given a Vector[BatchOp[X] forSome X] for which I know that there is one common type C such that they all are actually Vector[C], where is the scope in which this C is present as a usable type variable?

事实证明,BatchableOp[C] 本身在范围内有一个类型变量C。因此,我可以将一个方法 collectSameClassInstances 添加到 BachableOp[C],这个方法实际上将有一些类型 C 可用,它可以在返回类型。然后我可以立即将 collectSameClassInstances 的结果传递给 resolve 方法,然后我得到一个完全良性的 Vector[Result] 类型作为输出。

最后的评论:如果您决定编写任何具有 F 有界多态性存在性的代码,至少要确保您已经非常清楚地记录了究竟是什么 你在那里做什么,以及你将如何确保这种组合不会在代码库的任何其他部分逃逸。向用户公开这样的接口(interface)感觉不是个好主意。保持本地化,确保这些抽象不会泄漏到任何地方。

关于Scala:groupBy(_.getClass) 的转换结果,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48979420/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com