Scalaz Enum

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.

Using scala.Enumeration

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
@ object Direction extends Enumeration {
type Direction = Value
val NORTH, EAST, WEST, SOUTH = Value
}
defined object Direction
@ import Direction._
import Direction._
@ 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.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    @ object Direction {
    sealed trait Direction
    case object NORTH extends Direction
    case object EAST extends Direction
    case object WEST extends Direction
    case object SOUTH extends Direction
    }
    defined object Direction
    @ import Direction._
    import Direction._

    Advantages:

  • 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._
    @ import Scalaz._
    import Scalaz._
    // 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.

    1
    2
    3
    4
    @ 'B'.succ
    res6: Char = 'C'
    @ 'B'.pred
    res7: Char = 'A'

    It’s also possible to easily step through the enumeration.

    1
    2
    3
    4
    5
    6
    // 3 steps forwards
    @ 'B' -+- 3
    res8: Char = 'E'
    // 3 steps backwards
    @ 'B' --- 3
    res9: Char = '?'

    You can also get min and max values for an enumeration.

    1
    2
    3
    4
    @ implicitly[Enum[Int]].min
    res10: Option[Int] = Some(-2147483648)
    @ implicitly[Enum[Int]].max
    res11: Option[Int] = Some(2147483647)

    Let’s create our own enumeration using Scalaz Enum.

    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
    @ import scalaz._
    import scalaz._
    @ import Scalaz._
    import Scalaz._
    @ object Size {
    case class Size(val size: Int, val name: String)
    val SMALL = Size(0, "SMALL")
    val MEDIUM = Size(1, "MEDIUM")
    val LARGE = Size(2, "LARGE")

    implicit object SizeEnum extends Enum[Size] with Show[Size] {
    def order(s1: Size, s2: Size): Ordering = (s1.size compare s2.size) match {
    case -1 => Ordering.LT
    case 0 => Ordering.EQ
    case 1 => Ordering.GT
    }

    def succ(s: Size): Size = s match {
    case SMALL => MEDIUM
    case MEDIUM => LARGE
    case LARGE => SMALL
    }

    def pred(s: Size): Size = s match {
    case SMALL => LARGE
    case MEDIUM => SMALL
    case LARGE => MEDIUM
    }

    def zero: Size = SMALL

    override def shows(s: Size) = s.name

    override def max = Some(LARGE)

    override def min = Some(SMALL)
    }
    }
    defined object Size
    @ import Size._
    import Size._
    // 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.