Search code examples
scalashapeless

Cleaning up `case class` with `Option` FIelds


Given:

case class Foo(a: Option[Int], b: Option[Int], c: Option[Int], d: Option[Int])

I'd like to only allow constructing a Foo only if at least one of its arguments is Some, i.e. not all fields are None.

It would be quite a bit of code to write an Algebraic Data Type, and then make sub-classes for each variant:

sealed trait Foo
case class HasAOnly(a: Int)      extends Foo
case class HasAB(a: Int, b: Int) extends Foo
// etc...

Is there a cleaner, i.e. less code, way to address my problem using shapeless?


Solution

  • Thanks to the sealed abstract case class trick which Rob Norris recently publicised, you can keep the characteristics of your Foo case class but also provide your own smart constructor which returns an Option[Foo] depending on whether the given arguments pass all your criteria or not:

    sealed abstract case class Foo(
      a: Option[Int], b: Option[Int], c: Option[Int], d: Option[Int])
    
    object Foo {
      private class Impl(
        a: Option[Int], b: Option[Int], c: Option[Int], d: Option[Int])
        extends Foo(a, b, c, d)
    
      def apply(
        a: Option[Int],
        b: Option[Int],
        c: Option[Int],
        d: Option[Int]): Option[Foo] =
        (a, b, c, d) match {
          case (None, None, None, None) => None
          case _ => Some(new Impl(a, b, c, d))
        }
    }