(15) Enums

(15) Enums

Background

Classes are essentially bundles of functions operating on some common values represented as fields. They are a very useful abstraction since they allow the encapsulation of data.

image source: https://upload.wikimedia.org/wikipedia/commons/9/98/CPT-OOP-objects_andclasses-_attmeth.svg

file

But sometimes, we just need to compose and decompose pure data without any
associated functions.

We can use Enum – An enumeration refers to a group of named constants – a data type that contains only a finite set of named values.

image source: https://commons.wikimedia.org/wiki/File:CPT-OOP-objects_and_classes.svg

file

Enumeration – 1/5: Case Object

Case Object

A case object is like an object, but a case class has more features than a regular class. A case object has more features than a regular one. Its features include:

Case Object vs Object

  • It’s serializable
  • It has a default hashCode implementation
  • It has an improved toString implementation

Usage

Because of these features, case objects are primarily used in two places (instead of regular objects):

  • When creating enumerations
  • When creating containers for “messages” that you want to pass between other objects (such as with the Akka actors library)

Code Example

sealed trait DayOfWeek  
case object Sunday extends DayOfWeek  
case object Monday extends DayOfWeek  
case object Tuesday extends DayOfWeek  
case object Wednesday extends DayOfWeek  
case object Thursday extends DayOfWeek  
case object Friday extends DayOfWeek  
case object Saturday extends DayOfWeek  

Sunday // val res0: Sunday.type = Sunday  
Monday // val res1: Monday.type = Monday  

s"Today is $Saturday" // val res2: String = Today is Saturday

// Error: does not have #values  
// DayOfWeek.values

Enumeration – 2/5: case class

Example of Algebraic Data Types (ADTs)

 trait Expr  

object Expr {  
  case class Var(s: String) extends Expr  
  case class Number(n: Int) extends Expr  
  case class Sum(e1: Expr, e2: Expr) extends Expr  
  case class Prod(e1: Expr, e2: Expr) extends Expr  
}  

Expr.Number(10) // val res0: Expr.Number = Number(10)

// Error: does not have #values  
// Expr.values

Enumeration – 3/5: #enum key word

Advantage

Easy to write

Type 1 – Parameterized in the Enum Block

enum Expr {  
  case Var(s: String)  
  case Number(n: Int)  
  case Sum(e1: Expr, e2: Expr)  
  case Prod(e1: Expr, e2: Expr)  
}

// Error: does not have #values  
// Expr.values

This enum is equivalent to the case class hierarchy on the previous slide but is shorter since it avoids the repetitive notation of

class ... extends Expr 

Type 2 – Basic Enum

enum Color {  
  case Red  
  case Green  
  case Blue  
}  

Color.Red // val res1: Color = Red
Color.values //  val res2: Array[Color] = Array(Red, Green, Blue)

Type 3 – Parameterized Enums

Enumerations can take parameters and can define methods.


enum Direction(val dx: Int, val dy: Int) {  
  case Right extends Direction(1, 0)  
  case Up extends Direction(0, 1)  
  case Left extends Direction(-1, 0)  
  case Down extends Direction(0, -1)  

  def leftTurn = Direction.values((ordinal + 1) % 4)  

Direction.values // val res3: Array[Direction] = Array(Right, Up, Left, Down)

}  

val r = Direction.Right  
val u = r.leftTurn // u = Up  
val v = (u.dx, u.dy) // v = (1, 0)

Enumeration – 4/5: Extends Enumeration

Code Example

object Weekday extends Enumeration {  
  val Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday = Value  
}  

Weekday.Monday // val res0: Weekday.Value = Monday
Weekday.withName("Monday") // val res1: Weekday.Value = Monday

Weekday.values   
// val res2: Weekday.ValueSet = Weekday.ValueSet(Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday)

Provide a human-readable value

object Weekday2 extends Enumeration {  
  val Monday = Value("Mo.")  
  val Tuesday = Value("Tu.")  
  val Wednesday = Value("We.")  
  val Thursday = Value("Th.")  
  val Friday = Value("Fr.")  
  val Saturday = Value("Sa.")  
  val Sunday = Value("Su.")  
}  

Weekday2.Monday // val res2: Weekday2.Value = Mo.  

Weekday2.Monday.toString // val res3: String = Mo.  

Weekday.withName("Monday") // val res1: Weekday.Value = Monday

Weekday2.values  
// val res4: Weekday2.ValueSet = Weekday2.ValueSet(Mo., Tu., We., Th., Fr., Sa., Su.)

Issues

Due to type erasure, all enums have the same type at runtime.

// --------------------  
// Issue  

object CurrencyEnum extends Enumeration {  
  type Currency = Value  
  val GBP = Value(1, "GBP")  
  val EUR = Value  
}  

object InternetCodes extends Enumeration {  
  type CountryCode = Value  
  val EU, DE, CO = Value  
}  

import InternetCodes._  
import CurrencyEnum._  

 // Error  
object Methods {  
  def method(currency: Currency): Currency =  currency  
  def method(countryCode: CountryCode): CountryCode = countryCode  
}  

//|Double definition:  
//  |def method(currency: CurrencyEnum.Currency): CurrencyEnum.Currency in object Methods at line 2 and  
//  |def method(countryCode: InternetCodes.CountryCode): InternetCodes.CountryCode in object Methods at line 3

Enumeration – 5/5: Java Enum

Scala enumerations are not compatible with Java; that is to say, Java code won’t use an enumeration declared in Scala, regardless of which approach we chose to encode them.

Scala 3 solves these problems by unifying enums under a new syntax, which we can optionally make compatible with Java Enums:

object CurrencyADT(name: String, iso: String) extends java.lang.Enum {
    case EUR("Euro", "EUR")
    case USD("United States Dollar", "USD")
}

Pattern Matching

Pattern Matching Example:

enum DayOfWeek {  
  case Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday  
}  

import DayOfWeek.*  

def isWeekend(day: DayOfWeek) = day match  
 case Saturday | Sunday => true  
 case _ => false  

isWeekend(Monday) // val res0: Boolean = false  
isWeekend(Saturday) // val res1: Boolean = true

Summary of #values

  • #values is an immutable array in the companion object of an enum that contains all enum values
  • Only simple cases have ordinal numbers and show up in values. Parameterized cases do not.

X Enumeration – 1: Case Object
X Enumeration – 2: Case Class
X Enumeration – 3: #enum key word – Type 1 – Parameterized in the Enum Block
✓ Enumeration – 3: #enum key word – Type 2 – Basic Enum
✓ Enumeration – 3: #enum key word – Type 3 – Parameterized Enums
✓ Enumeration – 4: Extends Enumeration

Leave a Reply

Your email address will not be published. Required fields are marked *