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

Viewed 106

I have a vague feeling that such a method could exist (possibly in Cats?) - the purpose would be to turn an Iterable[T] (or Set[T] is what I'm actually interested in) into an Option[Iterable[S]] - the result being:

  • Some[Iterable[S]] : if all entries in the Iterable[T] were an instance of S
  • None : if any entries in the Iterable[T] were NOT an instance of S

I know there's a sort-of-similar method already available in Cats, granted by Alternative - it's 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

...that works nicely for a Set[Either[A,B] - but for this question I'm just interested in any old Set[T].

What about collect?

This code using the Scala standard library method collect will compile - but note that it will always produce a Set[S] - even if some of the setOfT weren't instances of S:

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

Consider shapeless type safe cast

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"))

If you wish similar power to collect and already have cats on classpath then here is one-liner

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

which types to Option[Set[Dog]].

As Luis Miguel Mejía Suárez points out in his comment, a method like the one I want isn't possible for arbitrary types, due to type-erasure.

In the end, I wrote code like this:

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

...would be happy to see better implementations though!

Related