Recently, Daniel Westheide wrote an interesting post about the abuse of the Option
type
in Scala.
You can find it here.
I couldn’t agree more with Daniel.
This short story is another example that demonstrates how using Option
is not always
the best option (pun intended).
I’m developing an advertising service for a customer using Scala.
A simplified version of the Ad
data structure is the following:
final case class Ad(
headline: String,
description1: String,
description2: String
)
At some point they told me we need to support, by adding the headline2
field,
two types of ad: standard and expanded.
They said: “If headline
, description1
, and description2
are used, it is a standard ad.
If headline
, headline2
, and description1
are used it is an expanded one.
Users won’t include headline2
when the ad is intended to be standard, and won’t include description2
when the ad is intended to be expanded.”
The optionality of the headline2
and description2
fields could put you in the wrong direction.
Indeed, you could think of changing the data structure as follows:
final case class Ad(
headline: String,
headline2: Option[String],
description1: String,
description2: Option[String]
)
This may sound fine but you could reach a much cleaner and type-safe solution using a simple Algebraic Data Type (ADT). Here’s how:
sealed trait Ad
object Ad {
final case class Standard(
headline: String,
description1: String,
description2: String
) extends Ad
final case class Expanded(
headline: String,
headline2: String,
description1: String
) extends Ad
}
This way you don’t need to inspect the headline2
and/or description2
value
to find out the ad type you’re dealing with.
Indeed, the ad type is encoded in the type system which is always a desiderata.
Bottom line: use Option
with caution.