Search code examples
scalaparametersenumsscala-3

Is it possible to pass a scala 3 enum as parameter?


I'm trying to create a method that takes a Scala 3 enum as parameter, but this does not work, is this possible?

I try to do something like

trait Named:
 def name: String

enum Birds(val name: String) extends Named:
  case Sparrow extends Birds("Sparrow")
  case Eagel extends Birds("Eagel")
  case Colibri extends Birds("Colibri")

enum Dogs(val name: String) extends Named:
  case Labrador extends Dogs("Labrador")
  case Dalmatian extends Dogs("Dalmatian")
  case Beagel extends Dogs("Beagel")

  
def showName[E <: Named](e: Enum[E]) = {
  e.values.map(v => println(v.name))
}

showName(Birds)
showName(Dogs)

https://scastie.scala-lang.org/B0RbB7CxRMagTBVodrSQVQ

Found:    Playground.Birds.type / Playground.Dogs.type
Required: Enum[E]

where:    E is a type variable with constraint <: Playground.Named

Solution

  • One way would be to use reflection and define a structural type with all the methods generated in the enum companion:

    import scala.reflect.Selectable.reflectiveSelectable
    
    type EnumOf[E] = {
      def values: Array[E]
      def valueOf(name: String): E
      def fromOrdinal(ordinal: Int): E
    }
     
    def showName[E <: Named](e: EnumOf[E]) = {
      println(e.values.map(_.name).mkString(", "))
    }
    
    showName(Birds) // Sparrow, Eagel, Colibri
    showName(Dogs) // Labrador, Dalmatian, Beagel
    

    We would call .values to obtain an Array[E], and then on each element call .name (because you defined def name on enum values buy not on enum companion object). Since it's array we have to convert it to List/Vector/String/etc since Array doesn't show anything useful with its toString method.

    To avoid reflection you'd have to define some common interface that companion object would implement (because OOTB there isn't one) like:

    trait EnumOf[E] {
      def values: Array[E]
      def valueOf(name: String): E
      def fromOrdinal(ordinal: Int): E
    }
    
    trait Named:
     def name: String
    
    enum Birds(val name: String) extends Named:
      case Sparrow extends Birds("Sparrow")
      case Eagel extends Birds("Eagel")
      case Colibri extends Birds("Colibri")
    object Birds extends EnumOf[Birds]
    
    enum Dogs(val name: String) extends Named:
      case Labrador extends Dogs("Labrador")
      case Dalmatian extends Dogs("Dalmatian")
      case Beagel extends Dogs("Beagel")
    object Dogs extends EnumOf[Dogs]
    
    // no need for reflectiveSelectable
    def showName[E <: Named](e: EnumOf[E]) = {
      println(e.values.map(_.name).mkString(", "))
    }