将 Scala Iterable[T] 转换为 Option[Iterable[S]],如果所有 T 都是 S 的实例则填充?

Converting a Scala Iterable[T] to an Option[Iterable[S]], populated if ALL the T are instances of S?

我有一种模糊的感觉,这种方法可能存在(可能在 Cats 中?)-目的是将 Iterable[T]Set[T] 是我真正感兴趣的 ) 到 Option[Iterable[S]] - 结果是:

我知道猫中已经有一种类似的方法,由 Alternative 授予 - 它是 separate:

import cats.implicits._
import alleycats.std.set._

val stringsAndInts: Set[Either[String, Int]] = Set(Right(6),Left("Foo"))
val (strings: Set[String], ints: Set[Int]) = stringsAndInts.separate

...这对 Set[Either[A,B] 很有效 - 但对于这个问题,我只对任何旧的 Set[T].

感兴趣

collect呢?

此代码使用 Scala 标准库方法 collect 将编译 - 但请注意,它始终会生成 Set[S] - 即使某些 setOfT 不是 setOfT 的实例S:

val setOfT: Set[T] = ???
val setOfS: Set[S] = setOfT.collect {case s: S => s}

正如 Luis Miguel Mejía Suárez 在他的评论中指出的那样,由于类型擦除,我想要的方法对于任意类型是不可能的。

最后,我写了这样的代码:

val animals: Set[Animal] = ???
val optDogs: Option[Set[Dog]] = animals.foldLeft(Option(Set.empty[Dog])) {
  case (oDogs, dog: Dog) => oDogs.map(_ + dog)
  case _ => None
}

...很高兴看到更好的实现!

考虑无形类型安全转换

import shapeless._
import syntax.typeable._

val xs: Set[Any] = Set("picard", "worf")

xs.cast[Set[Int]]
// res1: Option[Set[Int]] = None

xs.cast[Set[String]]
// res2: Option[Set[String]] = Some(value = Set("picard", "worf"))

如果您希望获得与 collect 类似的功能并且已经在类路径中添加了猫,那么这里是单行代码

import alleycats.std.set._
animals.map(implicitly[ClassTag[Dog]].unapply).sequence

哪些类型 Option[Set[Dog]]