为什么在这种更高种类的类型上调用 collect 时类型信息会丢失?

Why is the type information lost when calling collect on this higher-kinded type?

我试图熟悉 Scala 中的高级类型,因此我尝试实现这个简单的方法,该方法采用 Option 的可遍历并将其展平,就像通常展平它一样。但是,编译器会引发错误,因为函数 return 的类型是 Traversable[Any] 而不是 T[S]。为什么会这样,我怎样才能让它正常工作?

def flatten[S, T[_] <: Traversable[_]](list: T[Option[S]]): T[S] = {
  list.collect({ case Some(s) => s })
}

我想也许我错误地定义了 T 的类型,但我也尝试了 T[_]: TraversableT[X] <: Traversable[X] 但它们也没有用。

当然,这行得通:

def flatten[S](list: Traversable[Option[S]]): Traversable[S] = {
  list.collect({ case Some(s) => s })
}

但我不想丢失 return 类型的输入类型信息(调用 flatten(List[Option[T]]) 应该 return List[T].

这是因为 collect 没有 return 一个 T,它 return 只有一个 Traversable。特征 Traversable 不知道 class 继承它的任何类型。

另外,你的 higher-kinded 类型是错误的,它应该是 T[x] <: Traversable[x] 以避免存在主义的怪异。你可以这样做:

def flatten[S, T[x] <: Traversable[x]](list: T[Option[S]])(
  implicit ev: collection.generic.CanBuildFrom[Traversable[Option[S]], S, T[S]]
): T[S] = list.collect { case Some(s) => s }

,或者您最好听从 Luis Miguel Mejía Suárez 关于使用 typeclasses 的建议。如果可能,我还建议使用 Scala 2.13。

trait Flatten[F[_]] {
  def flatten[S](list: F[Option[S]]): F[S]
}
object Flatten {
  def flatten[S, F[_]](list: List[Option[S]])(implicit f: Flatten[F]) = f.flatten(list)
  
  implicit val flattenList = new Flatten[List] {
    def flatten[S](list: List[Option[S]]) = list.collect { case Some(s) => s }
  }
}