Scala replacement for a Java enum pattern I use all the time

scala

Solution

I managed to make up this kind-of replacement for your case:

sealed class DataField(val xml: String, val db: String, val sys: String)

object DataField {
  case object Field1 extends DataField("xmlField1", "dbField1", "system1Field")
  case object Field2 extends DataField("xmlField2", "dbField2", "system2Field")
  case object Field3 extends DataField("xmlField3", "dbField3", "system3Field")

  val values = List(Field1, Field2, Field3)

  def valueOfXml(xml: String) =
    values.find(_.xml == xml).get
}

The annoying thing is that we have to manually create the `values` list. In this case however, we can do some macro hacking to reduce the boilerplate a bit:

import scala.language.experimental.macros
import scala.reflect.macros.Context

object Macros {
  def caseObjectsFor[T]: List[T] = macro caseObjectsFor_impl[T]

  def caseObjectsFor_impl[T: c.WeakTypeTag](c: Context): c.Expr[List[T]] = {
    import c.universe._

    val baseClassSymbol = weakTypeOf[T].typeSymbol.asClass
    val caseObjectSymbols = baseClassSymbol.knownDirectSubclasses.toList.collect {
      case s if s.isModuleClass && s.asClass.isCaseClass => s.asClass.module
    }

    val listObjectSym = typeOf[List.type].termSymbol
    c.Expr[List[T]](Apply(Ident(listObjectSym), caseObjectSymbols.map(s => Ident(s))))
  }
}

Then we can do this:

val values = Macros.caseObjectsFor[DataField]

instead of manually listing all the case objects.

For this to work, it is essential that the base class is declared as `sealed`.

Problem

In Java, I do a lot of data integration work. One thing that comes up all the time is mapping data between multiple systems. So i'm constantly doing things like this ``` public enum DataField{ Field1("xmlField", "dbField", "system1Field"; private String xml; private String db; private String sys; private DataField(String xml, String db, String sys){ this.xml = xml; this.db = db; this.sys = sys; } public getXml(){ return this.xml; } public static DataField valueOfXml(String xml){ for (DataField d : this.values()){ if (d.xml.equals(xml)){ return d;} } } bla, bla bla } ``` What this allows me to do is put the field name DataField in all my messaging and be able to map what that field is called in multiple systems. So in my XML, it may be `firstname`, in my database, it may be called `first_name` but in my external interface system, it may be called `first`. This pattern pulls all of that together very nicely and makes messaging in these types of systems very easy in a tight, type safe way. Now I don't remember why Scala changed the enumeration implementation but I remember it made sense when I read it. But the question is, what would I use in Scala to replace this design pattern? I hate to lose it because it is very useful and fundamental to a lot of systems I write on a given day. thanks

Original source

Related problems