Scala doesn't have type-safe enum
s like Java has. Given a set of related constants, what would be the best way in Scala to represent those constants?

- 72,696
- 27
- 242
- 420

- 202,709
- 46
- 318
- 350
-
2Why not just using java enum? This is one of the few things I still prefer to use plain java. – Max Jan 01 '16 at 01:39
-
1I've written a small overview about scala Enumeration and alternatives, you may find it useful: pedrorijo.com/blog/scala-enums/ – pedrorijo91 Dec 09 '16 at 22:05
7 Answers
I must say that the example copied out of the Scala documentation by skaffman above is of limited utility in practice (you might as well use case object
s).
In order to get something most closely resembling a Java Enum
(i.e. with sensible toString
and valueOf
methods -- perhaps you are persisting the enum values to a database) you need to modify it a bit. If you had used skaffman's code:
WeekDay.valueOf("Sun") //returns None
WeekDay.Tue.toString //returns Weekday(2)
Whereas using the following declaration:
object WeekDay extends Enumeration {
type WeekDay = Value
val Mon = Value("Mon")
val Tue = Value("Tue")
... etc
}
You get more sensible results:
WeekDay.valueOf("Sun") //returns Some(Sun)
WeekDay.Tue.toString //returns Tue

- 133,303
- 56
- 317
- 449
-
7
-
36@macias `valueOf`'s replacement is `withName`, which doesn't return an Option, and throws a NSE if there is no match. What the! – Bluu Jan 31 '12 at 19:00
-
6@Bluu You can add valueOf yourself: def valueOf(name: String) = WeekDay.values.find(_.toString == name) to have an Option – centr Jan 11 '14 at 01:45
-
@centr When I try to create a `Map[Weekday.Weekday, Long]` and add a value say `Mon` to it the compiler throws an invalid type error. Expected Weekday.Weekday found Value? Why does this happen? – Sohaib Jun 14 '15 at 18:30
-
-
-
My IDE gives me a warning Type annotation required for public member. Do you know how to fix it? – Alex Jun 11 '18 at 21:10
http://www.scala-lang.org/docu/files/api/scala/Enumeration.html
Example use
object Main extends App {
object WeekDay extends Enumeration {
type WeekDay = Value
val Mon, Tue, Wed, Thu, Fri, Sat, Sun = Value
}
import WeekDay._
def isWorkingDay(d: WeekDay) = ! (d == Sat || d == Sun)
WeekDay.values filter isWorkingDay foreach println
}
-
2Seriously, Application should not be used. It was NOT fixed; a new class, App, was introduced, which does not have the problems Schildmeijer mentioned. So do "object foo extends App { ... }" And you have immediate access to command-line arguments through the args variable. – AmigoNico Jul 25 '12 at 02:55
-
scala.Enumeration (which is what you are using in your "object WeekDay" code sample above) does not offer exhaustive pattern matching. I have researched all the different enumeration patterns currently being used in Scala and give and overview of them in this StackOverflow answer (including a new pattern which offers the best of both scala.Enumeration and the "sealed trait + case object" pattern: http://stackoverflow.com/a/25923651/501113 – chaotic3quilibrium Sep 19 '14 at 20:01
There are many ways of doing.
1) Use symbols. It won't give you any type safety, though, aside from not accepting non-symbols where a symbol is expected. I'm only mentioning it here for completeness. Here's an example of usage:
def update(what: Symbol, where: Int, newValue: Array[Int]): MatrixInt =
what match {
case 'row => replaceRow(where, newValue)
case 'col | 'column => replaceCol(where, newValue)
case _ => throw new IllegalArgumentException
}
// At REPL:
scala> val a = unitMatrixInt(3)
a: teste7.MatrixInt =
/ 1 0 0 \
| 0 1 0 |
\ 0 0 1 /
scala> a('row, 1) = a.row(0)
res41: teste7.MatrixInt =
/ 1 0 0 \
| 1 0 0 |
\ 0 0 1 /
scala> a('column, 2) = a.row(0)
res42: teste7.MatrixInt =
/ 1 0 1 \
| 0 1 0 |
\ 0 0 0 /
2) Using class Enumeration
:
object Dimension extends Enumeration {
type Dimension = Value
val Row, Column = Value
}
or, if you need to serialize or display it:
object Dimension extends Enumeration("Row", "Column") {
type Dimension = Value
val Row, Column = Value
}
This can be used like this:
def update(what: Dimension, where: Int, newValue: Array[Int]): MatrixInt =
what match {
case Row => replaceRow(where, newValue)
case Column => replaceCol(where, newValue)
}
// At REPL:
scala> a(Row, 2) = a.row(1)
<console>:13: error: not found: value Row
a(Row, 2) = a.row(1)
^
scala> a(Dimension.Row, 2) = a.row(1)
res1: teste.MatrixInt =
/ 1 0 0 \
| 0 1 0 |
\ 0 1 0 /
scala> import Dimension._
import Dimension._
scala> a(Row, 2) = a.row(1)
res2: teste.MatrixInt =
/ 1 0 0 \
| 0 1 0 |
\ 0 1 0 /
Unfortunately, it doesn't ensure that all matches are accounted for. If I forgot to put Row or Column in the match, the Scala compiler wouldn't have warned me. So it gives me some type safety, but not as much as can be gained.
3) Case objects:
sealed abstract class Dimension
case object Row extends Dimension
case object Column extends Dimension
Now, if I leave out a case on a match
, the compiler will warn me:
MatrixInt.scala:70: warning: match is not exhaustive!
missing combination Column
what match {
^
one warning found
It's used pretty much the same way, and doesn't even need an import
:
scala> val a = unitMatrixInt(3)
a: teste3.MatrixInt =
/ 1 0 0 \
| 0 1 0 |
\ 0 0 1 /
scala> a(Row,2) = a.row(0)
res15: teste3.MatrixInt =
/ 1 0 0 \
| 0 1 0 |
\ 1 0 0 /
You might wonder, then, why ever use an Enumeration instead of case objects. As a matter of fact, case objects do have advantages many times, such as here. The Enumeration class, though, has many Collection methods, such as elements (iterator on Scala 2.8), which returns an Iterator, map, flatMap, filter, etc.
This answer is essentially a selected parts from this article in my blog.

- 295,120
- 86
- 501
- 681
-
"... not accepting non-symbols where a symbol is expected" > I'm guessing you mean that `Symbol` instances cannot have spaces or special characters. Most people when first encountering the `Symbol` class probably think so, but is actually incorrect. `Symbol("foo !% bar -* baz")` compiles and run perfectly fine. In other words you can perfectly create `Symbol` instances wrapping *any* string (you just cannot do it with the "single coma" syntactic sugar). The only thing that `Symbol` does guarantee is the uniqueness of any given symbol, making it marginally faster to compare and match over. – Régis Jean-Gilles Oct 29 '19 at 10:27
-
@RégisJean-Gilles No, I mean that you cannot pass a `String`, for example, as argument to a `Symbol` parameter. – Daniel C. Sobral Dec 02 '19 at 18:45
-
Yes, I understood that part, but it's a pretty moot point if you replace `String` with another class that is basically a wrapper around a string and can be freely converted in both directions (as is the case for `Symbol`). I guess that's what you meant when saying "It won't give you any type safety", it just wasn't very clear given that OP explicitly asked for type safe solutions. I wasn't sure if at the time of writing you knew that not only is it not type safe because those are not enums at all, but **also** `Symbol`s don't even guarantee that the passed argument won't have special chars. – Régis Jean-Gilles Jan 06 '20 at 09:32
-
1To elaborate, when you say "not accepting non-symbols where a symbol is expected", it can be read either as "not accepting values that are not instances of Symbol" (which is obviously true) or "not accepting values that are not plain identifier-like strings, aka 'symbols'" (which is not true, and is a misconception that pretty much anybody has the first time we encounter scala symbols, due to the fact that the first encounter is though the special `'foo` notation which **does** preclude non-identifier strings). This is this misconception that I wanted to dispel for any future reader. – Régis Jean-Gilles Jan 06 '20 at 09:37
-
@RégisJean-Gilles I meant the former, the one which is obviously true. I mean, it's obviously true for anyone used to static typing. Back then there was a lot of discussion on the relative merits of static and "dynamic" typing, and a lot of people interested in Scala came from a dynamic typing background, so it I thought it _didn't_ go without saying. I wouldn't even think of making that remark nowadays. Personally, I think Scala's Symbol is ugly and redundant, and never use it. I'm upvoting your last comment, since it's a good point. – Daniel C. Sobral Jan 22 '20 at 18:41
-
"I think Scala's Symbol is ugly and redundant" > oh I could not agree more on that one. And we seem to be in good company as it appears that it's going away in scala 3. – Régis Jean-Gilles Jan 27 '20 at 08:17
A slightly less verbose way of declaring named enumerations:
object WeekDay extends Enumeration("Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat") {
type WeekDay = Value
val Sun, Mon, Tue, Wed, Thu, Fri, Sat = Value
}
WeekDay.valueOf("Wed") // returns Some(Wed)
WeekDay.Fri.toString // returns Fri
Of course the problem here is that you will need to keep the ordering of the names and vals in sync which is easier to do if name and val are declared on the same line.

- 11,547
- 2
- 47
- 36
-
11This looks cleaner at first glance, but has the disadvantage of requiring the maintainer to keep the oder of both lists in sync. For the days of week example, it doesn't appear likely. But in general, the a new value could be inserted, or one deleted and the two lists could be out of sync, in which case, subtle bugs could be introduced. – Brent Faust May 23 '13 at 22:02
-
1Per the prior comment, the risk is the two different lists can silently go out of sync. While it’s not an issue for your current small example, if there are many more members (like in the dozens to hundreds), the odds of the two lists silently going out of sync is substantially higher. Also scala.Enumeration cannot benefit from Scala's compile time exhaustive pattern matching warnings/errors. I’ve created a StackOverflow answer which contains a solution performing a runtime check to ensure the two lists remain in sync: http://stackoverflow.com/a/25923651/501113 – chaotic3quilibrium Sep 20 '14 at 00:08
You can use a sealed abstract class instead of the enumeration, for example:
sealed abstract class Constraint(val name: String, val verifier: Int => Boolean)
case object NotTooBig extends Constraint("NotTooBig", (_ < 1000))
case object NonZero extends Constraint("NonZero", (_ != 0))
case class NotEquals(x: Int) extends Constraint("NotEquals " + x, (_ != x))
object Main {
def eval(ctrs: Seq[Constraint])(x: Int): Boolean =
(true /: ctrs){ case (accum, ctr) => accum && ctr.verifier(x) }
def main(args: Array[String]) {
val ctrs = NotTooBig :: NotEquals(5) :: Nil
val evaluate = eval(ctrs) _
println(evaluate(3000))
println(evaluate(3))
println(evaluate(5))
}
}

- 9,262
- 4
- 40
- 73
-
-
2The "sealed trait + case objects" pattern has issues which I detail in a StackOverflow answer. However, I did figure out how to resolve all the issues related to this pattern which is also covered in the thread: http://stackoverflow.com/a/25923651/501113 – chaotic3quilibrium Sep 19 '14 at 23:54
Starting from Scala 3, there is now enum keyword which can represent a set of constants (and other use cases)
enum Color:
case Red, Green, Blue
scala> val red = Color.Red
val red: Color = Red
scala> red.ordinal
val res0: Int = 0

- 7,456
- 1
- 22
- 32
In Scala it is very comfortable with https://github.com/lloydmeta/enumeratum
Project is really good with examples and documentation
Just this example from their docs should makes you interested in
import enumeratum._
sealed trait Greeting extends EnumEntry
object Greeting extends Enum[Greeting] {
/*
`findValues` is a protected method that invokes a macro to find all `Greeting` object declarations inside an `Enum`
You use it to implement the `val values` member
*/
val values = findValues
case object Hello extends Greeting
case object GoodBye extends Greeting
case object Hi extends Greeting
case object Bye extends Greeting
}
// Object Greeting has a `withName(name: String)` method
Greeting.withName("Hello")
// => res0: Greeting = Hello
Greeting.withName("Haro")
// => java.lang.IllegalArgumentException: Haro is not a member of Enum (Hello, GoodBye, Hi, Bye)
// A safer alternative would be to use `withNameOption(name: String)` method which returns an Option[Greeting]
Greeting.withNameOption("Hello")
// => res1: Option[Greeting] = Some(Hello)
Greeting.withNameOption("Haro")
// => res2: Option[Greeting] = None
// It is also possible to use strings case insensitively
Greeting.withNameInsensitive("HeLLo")
// => res3: Greeting = Hello
Greeting.withNameInsensitiveOption("HeLLo")
// => res4: Option[Greeting] = Some(Hello)
// Uppercase-only strings may also be used
Greeting.withNameUppercaseOnly("HELLO")
// => res5: Greeting = Hello
Greeting.withNameUppercaseOnlyOption("HeLLo")
// => res6: Option[Greeting] = None
// Similarly, lowercase-only strings may also be used
Greeting.withNameLowercaseOnly("hello")
// => res7: Greeting = Hello
Greeting.withNameLowercaseOnlyOption("hello")
// => res8: Option[Greeting] = Some(Hello)

- 459
- 4
- 4