If you’ve been working with Scala for a while, you might have come across a few “problems” with the built in Enumeration that’s provided out-of-the-box. This is especially true if you have colleagues who come from a Java background and yearn for the Java-style Enum that gave them lots of power and flexibility.

A quick search on the internet for “Scala enumeration alternative” will yield a lot of results (perhaps on StackOverflow) where people have cooked up their own implementation of enumerations, usually built on sealed traits. Personally, I found most of them to be either too inconvenient to use, too over-powered, or too complicated, and I really didn’t want to have to copy-paste enum-related code into all my projects.

Thus Enumeratum was born.

Enumeratum aims to be simple to use, idiomatic, small (LoC), yet flexible enough to allow Scala devs to make power enums if they so wish. It is also Mavenised for easy import into any project.

To use it, simply add it as a dependency

1
2
3
4
5
val enumeratumVersion = "1.4.2"  // latest version number can be found on the Maven Central version badge on the Github repo.
libraryDependencies ++= Seq(
    "com.beachape" %% "enumeratum" % enumeratumVersion,
    "com.beachape" %% "enumeratum-play" % enumeratumVersion // if you are using Play and want to avoid boilerplate
)

Then

Enumeratum example code (enumeratum_example.scala) download
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
// For use in a REPL

import enumeratum._

sealed trait Phone {
  def call(number: Int): String
} extends EnumEntry

case object Phone extends Enum[Phone] {

  case object Android extends Phone {
    def call(number: Int) = "This is Larry Page."
  }

  case object Iphone extends Phone {
    def call(number: Int) = "This is Steve Jobs."
  }

  case object WindowsPhone extends Phone {
    def call(number: Int) = "This is Bill Gates."
  }

  val values = findValues

}

import Phone._

// Use as needed.

val myPhone = Iphone

// Get exhaustive match warnings
def rate(phone: Phone): String = phone match {
  case Android => "Great!"
  case Iphone => "Awesome!"
}

/*
<console>:17: warning: match may not be exhaustive.
It would fail on the following input: WindowsPhone
*/

You get nice things like exhaustive match warnings at compile-time, enums with methods, no more Enum-value type erasure, and other nice stuff.

How it works

Some of the solutions for custom enums out there are based on macros that reflect at compile time using knownDirectSubclasses to find enum values, but as of writing, there is a 2 year old bug for that method.

As a result, Enumeratum uses another method of finding enum values: looking in an enclosed object to find the enum values. The macro behind findValues does this for you so that you don’t have to maintain your own collection of enum values, which is both error-prone and tedious.

Play

If you want to use Enumeratum in a Play app, you may as well add enumeratum-play as a dependency instead so that you can use the PlayEnum[A] trait (instead of Enum[A]), which will give you nice things like QueryStringBinders, PathBinders, form mappers, and Json Reads/Writes/Formats. To make use of this integration, just extend from PlayEnum instead of Enum in the above example.

This means less boilerplate in your project, which is A Good Thing, right?

Limitations

There are a few limitations with Enumeratum:

  • ~~Ordinality is not taken care of. From what I’ve seen, this is one of the least-used functions of Enums in general. That said, nothing is stoping you from defining an Ordering in your companion object for your sealed trait.~~
  • Because the Enum values are case objects, they will be inferred to have their own specific type, which may cause problems with compilation for typeclasses that are not contravariant. In that case, simply help the compiler by adding a type (e.g. val myPhone: Phone = Iphone)
  • ~~The method withName relies on the toString method of the Enum values for lookup. Make sure to override this if you have specific requirements.~~

Update 2016/04/22 Crossed out a bunch of limitations that no longer apply.

Enjoy

I hope Enumeratum can help you out of your Enumeration woes. Have a look, play around, and send a PR or two !

Comments