In this post we’ll look at how to create enum using Scalaz. However, we’ll first look at how to create enum using standard Scala library.
Enum - Scala Way
An enum type is a special data type that enables for a variable to be a set of predefined constants. The variable must be equal to one of the values that have been predefined for it. Common examples include compass directions (values of NORTH, SOUTH, EAST, and WEST) and the days of the week.[1]
There are a couple of ways in which you can create enum in Scala.
Defines a finite set of values specific to the enumeration. Typically these values enumerate all possible forms something can take and provide a lightweight alternative to case classes.
Let’s see an example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
@ objectDirectionextendsEnumeration{ typeDirection= Value valNORTH, EAST, WEST, SOUTH = Value } defined objectDirection @ importDirection._ importDirection._ @ Direction withName "NORTH" res2: Value = NORTH @ Direction.values foreach println NORTH EAST WEST SOUTH
Advantages:
Lightweight.
Easy to use.
Iterable.
Disadvantages:
They only exist as unique values. You cannot add behavior to them.
Using Case Objects
Enum can also be created using sealed trait. A sealed trait requires all the classes or traits extending it be in the same file in which it is defined.
Each value is an instance of its own type and it's possible to add behavior to them.
Disadvantages:
A case object generates a lot more code than `Enumeration`.
Not iterable.
Enum - Scalaz Way
Scalaz allows creating enum using Enum trait. Scalaz enums are more powerful than the standard Scala enums. Let’s see some examples of creating Scalaz enum[2]:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
@ import scalaz._ import scalaz._ @ importScalaz._ importScalaz._ // as a List @ 'a' |-> 'z' res3: List[Char] = List( 'a', 'b', 'c', 'd', 'e', 'f', ... // as a Scalaz stream @ 'a' |=> 'z' res4: EphemeralStream[Char] = scalaz.EphemeralStream$$anon$5@2fb4925c
Scalaz also provides us with succ and pred to get the successor and predecessor.
defsucc(s: Size): Size = s match { caseSMALL => MEDIUM caseMEDIUM => LARGE caseLARGE => SMALL }
defpred(s: Size): Size = s match { caseSMALL => LARGE caseMEDIUM => SMALL caseLARGE => MEDIUM }
defzero: Size = SMALL
overridedefshows(s: Size) = s.name
overridedefmax= Some(LARGE)
overridedefmin= Some(SMALL) } } defined objectSize @ importSize._ importSize._ // step through @ SMALL -+- 1 res4: Size = Size(1, "MEDIUM") // as a list @ SMALL |-> LARGE res5: List[Size] = List(Size(0, "SMALL"), Size(1, "MEDIUM"), Size(2, "LARGE")) // show @ SMALL.println SMALL // max @ implicitly[Enum[Size]].max res6: Option[Size] = Some(Size(2, "LARGE")) // min @ implicitly[Enum[Size]].min res7: Option[Size] = Some(Size(0, "SMALL"))
Notice that since we’re using implicitly[], we don’t have to provide the exact name of the typeclass extending Enum[Size], the compiler will figure it out from the implicits within scope.
Conclusion
Although Scalaz Enum may seem like a long-winded way to do something trivial, they offer you a lot more flexibility and brevity over vanilla Scala enum. However, I believe that the full force of Scalaz Enum is needed only when you have ordered elements like size. For cases like compass directions, Scala enum are better-suited.