Sealed traits and their implementations

I do know that all possible implementations of a sealed trait must reside in the very same source code file where the trait is decelerated:

sealed trait Color
case class Blue() extends Color
case class Green() extends Color
case class Red() extends Color

To keep concerns together, I am wondering if it was the right style to declare the possible members as inner-classes of the companion object:

sealed trait Color

object Color {
  case class Blue() extends Color
  case class Green() extends Color
  case class Red() extends Color
}

What do you think?

Yeah that is a common pattern.

Just two things, make your case classes final and if they do not have arguments, use case objects instead.

2 Likes

I can’t wait to be able to replace all of that with the following equivalent in Scala 3:

enum Color {
  case Red, Green, Blue
}

which is just syntactic sugar for

enum Colour {
  case Red
  case Green
  case Blue
}
1 Like

I always use the pattern:

package my.pkg

sealed trait MyType

object MyType {
  case class SubType1(params...) extends MyType
  case object SubType2 extends MyType
  ...
}

as it doesn’t pollute the package namespace. After doing import my.pkg._ I only get MyType and none of the subtypes. This is helpful if package contains more members, like a similar MyType2 with its own subtypes or any other classes or objects. If I want to have the subtypes directly accessible then I write import my.pkg.MyType._ - that also clearly communicates the intent that I want to work with them.

2 Likes