As mentioned in the comments defining such a method in Scala 2 would not be straightforward to do in a typesafe manner. First of all, recursive types are not supported directly in Scala 2, so you'd have to operate on List[Any]
and use runtime reflection to distinguish if the element is a list or an integer.
Recently released Scala 3 has many improvements in its type system, so I wondered that maybe it would be possible to implement such a method there? I tried and I think I was able to achieve usable implementation.
First of all, I wondered if it would be possible to implement recursive union type (a similar thing is possible in typescript):
type ListOr[A] = A | List[ListOr[A]]
unfortunately, such type was raising compiler error:
illegal cyclic type reference: alias ... of type ListOr refers back to the type itself
That was disappointing, but after some digging, I found that I could define such recursive type as:
type ListOr[A] = A match {
case AnyVal => AnyVal | List[ListOr[AnyVal]]
case _ => A | List[ListOr[A]]
}
and it was usable:
val ints: ListOr[Int] = List(List(1), 2, 3, List(List(4, List(5)), 6), 7, 8, List(9))
val strings: ListOr[String] = List(List("A", "B", "C"), List(List("D", List("E")), "F"), "G", "H", List("I"), "J")
so now I needed to just implement the flattening function:
//I needed class tag for A to be able to do a match
def deepFlatten[A: ClassTag](s: ListOr[A]): List[A] =
s match
case a: A => List(a)
case ls: List[_ <: ListOr[A]] => ls.flatMap(deepFlatten(_))
and it seemed to be working correctly:
@main
def main =
val i: List[Int] = deepFlatten[Int](ints) //List(1, 2, 3, 4, 5, 6, 7, 8, 9)
val j: List[String] = deepFlatten[String](strings)//List(A, B, C, D, E, F, G, H, I, J)
Obviously, such implementation could be improved (it's not tail-recursive), but it's doing its job.
Since I'm Scala 3 novice I'm not sure if that's the best implementation, but it's definitely possible to implement such arbitrarily deep flattening functions as type-safe.
Scastie with the solution.