Search code examples
scalafunctional-programmingscala-collectionsdiscriminated-union

Collection of Union types scala


Is it possible in scala to have a collection of a union types. There are a few approaches to union types discussed here The top rated answer feels the most native, i have something like this:

sealed trait StringOrNumber[T]
object StringOrNumber {
    implicit object IntWitness extends StringOrNumber[Int]
    implicit object StringWitness extends StringOrNumber[String]
}

but when i try to make a map that contains both

val m: Map[String, Any] = Map("str" -> "hellp", "int" -> 32)

The scala compiler sees it as a map of [String,Any] Is there a way to tell the scala compiler this is a map [String, StringOrNumber]

Edit:

I dont think using the approach above is possible to create a collection of string or union. I think it needs to be another approach to a union type since the above is akin to overloaded methods rather than a true union type in the type system


Solution

  • The closest emulation of runtime union types, you can do in the current version of Scala, is to wrap types of the union in case classes extending some sealed trait. It's boilerplate-y and adds an extra wrapper layer over AnyRef types, but it works, it's better than just using Any, and you can also add implicit conversions from union types:

    sealed trait StringOrNumber
    object StringOrNumber {
      case class IsNumber(i: Int) extends StringOrNumber
      case class IsString(s: String) extends StringOrNumber
    
      implicit def isNumber(i: Int): StringOrNumber = IsNumber(i)
      implicit def isString(s: String): StringOrNumber = IsString(s)
    }
    

    Now you can define your Map:

    scala> val m: Map[String, StringOrNumber] = Map("str" -> "hellp", "int" -> 32)
    m: Map[String,StringOrNumber] = Map(str -> IsString(hellp), int -> IsNumber(32))