public
Created — forked from viktorklang/ScalaEnum.scala

DIY Scala Enums (with optional exhaustiveness checking)

  • Download Gist
ScalaEnum.scala
Scala
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
trait Enum { //DIY enum type
import java.util.concurrent.atomic.AtomicReference //Concurrency paranoia
 
type EnumVal <: Value //This is a type that needs to be found in the implementing class
 
private val _values = new AtomicReference(Vector[EnumVal]()) //Stores our enum values
 
//Adds an EnumVal to our storage, uses CCAS to make sure it's thread safe, returns the ordinal
private final def addEnumVal(newVal: EnumVal): Int = { import _values.{get, compareAndSet => CAS}
val oldVec = get
val newVec = oldVec :+ newVal
if((get eq oldVec) && CAS(oldVec, newVec)) newVec.indexWhere(_ eq newVal) else addEnumVal(newVal)
}
 
def values: Vector[EnumVal] = _values.get //Here you can get all the enums that exist for this type
 
//This is the trait that we need to extend our EnumVal type with, it does the book-keeping for us
protected trait Value { self: EnumVal => //Enforce that no one mixes in Value in a non-EnumVal type
final val ordinal = addEnumVal(this) //Adds the EnumVal and returns the ordinal
 
def name: String //All enum values should have a name
 
override def toString = name //And that name is used for the toString operation
override def equals(other: Any) = other.asInstanceOf[AnyRef] eq this
override def hashCode = 31 * (this.getClass.## + name.## + ordinal)
}
}
 
//And here's how to use it, if you want compiler exhaustiveness checking
object Foos extends Enum {
sealed trait EnumVal extends Value /*{ you can define your own methods etc here }*/
 
val F = new EnumVal { val name = "F" }
val X = new EnumVal { val name = "X" }
}
 
/**
scala> Foos.values.find(_.name == "F")
res3: Option[Foos.EnumVal] = Some(F)
 
scala> Foos.X.ordinal
res4: Int = 1
 
scala> def doSmth(foo: Foos.EnumVal) = foo match {
case Foos.X => println("pigdog")
}
 
<console>:10: warning: match is not exhaustive!
missing combination $anon$1
missing combination $anon$2
 
scala> def doSmth(foo: Foos.EnumVal) = foo match {
case Foos.X => println("pigdog")
case Foos.F => println("dogpig")
}
doSmth: (foo: Foos.EnumVal)Unit
**/
 
//But if you don't care about getting exhaustiveness warnings, you can do:
 
object Foos extends Enum {
case class EnumVal private[Foos](name: String) extends Value /* { you can define your own methods and stuff here } */
 
val F = EnumVal("F")
val X = EnumVal("X")
}
 
/**
Which is a bit less boilerplatey.
 
 
Cheers,
**/

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.