Concise enumeration in Scala

Julien Truffaut

16th February 2023

In this blog post, I would like to share a simple trick to create a concise enumeration in Scala.

The Problem

We want to create an enumeration and associate each possibility to a single value, e.g. map the object Euro to the String EUR or the object Equity to the number 10.

Why? we may want to use this value in a serialization format (column in db, JSON, ...) or display it to our users.

I will now present a few solutions to this problem and discuss their pros and cons.

Option 1: Define in the case object

sealed trait OrderStatus { def id: String } object OrderStatus { case object Draft extends OrderStatus { def id: String = "DRAFT" } case object Submitted extends OrderStatus { def id: String = "SUBMITTED" } case object Delivered extends OrderStatus { def id: String = "DELIVERED" } }

Option 2: Define in the sealed trait

sealed trait OrderStatus { import OrderStatus._ def id: String = this match { case Draft => "DRAFT" case Submitted => "SUBMITTED" case Delivered => "DELIVERED" } } object OrderStatus { case object Draft extends OrderStatus case object Submitted extends OrderStatus case object Delivered extends OrderStatus }

Both options have their inconvenience. With option 1, it is difficult to see at glance which are the three possible statuses. While with option 2, we need to do a pattern matching and import all the case objects inside the trait.

Could we instead define the order status id at the same time we define the case object?

Yes, we can do this by adding a constructor to OrderStatus!

Option 3: Define in the constructor

sealed abstract class OrderStatus(val id: String) object OrderStatus { case object Draft extends OrderStatus("DRAFT") case object Submitted extends OrderStatus("SUBMITTED") case object Delivered extends OrderStatus("DELIVERED") }

This is personally my favorite approach for simple enumeration as it keeps the code concise and clear. A few things to note:

  1. In Scala 2, traits cannot have a constructor, so we need to use an abstract class instead. In Scala 3, we don't have this limitation.
  2. constructor arguments are private by default, so we need to define id with a val keyword to make it visible outside the class.

Don't hesitate to comment or share your favorite approach on reddit.

Edit: Someone asked me how it would look like in Scala 3 using the new enum keyword.

enum OrderStatus(val id: String) { case Draft extends OrderStatus("DRAFT") case Submitted extends OrderStatus("SUBMITTED") case Delivered extends OrderStatus("DELIVERED") }

Scala 3 enum is definitely an improvment!

Subscribe to receive the latest Scala jobs in your inbox

Receive a weekly overview of Scala jobs by subscribing to our mailing list

© 2024, All rights reserved.