Home > Software engineering >  Scala generic case class with optional field
Scala generic case class with optional field

Time:06-02

I have the following generic case class to model a resources in an HTTP API (we use Akka HTTP):

case class Job[Result](
   id: String,
   result: Option[Result] = None,
   userId: String,
)

and want to specify multiple, named, variations of a Job, were some of them provide a result, while others don't:

case class FooResult(data: String)
type FooJob = Job[FooResult]

// BarJob does not have any result, thus result should be None
case class BarJob = Job[/*what do to?*/]

my question is, is there any way to define Job as a generic case class where the type parameter only needs to be specified when the field result is Some ? What I would like to do is something like:


// result is by default None in Job, so I don't want to specify any type here
case class BarJob = Job

Or perhaps there's better ways to do this, rather than using type aliases?

CodePudding user response:

One option is to use base traits for Jobs with and without results:

trait Job {
  def id: String
  def userId: String
}

trait JobWithResult[T] extends Job {
  def result: T
}

case class FooResult(data: String)
case class FooJob(id: String, userId: String, result: FooResult) extends JobWithResult[FooResult]

case class BarJob(id: String, userId: String) extends Job

You can then use match on a Job to see whether it has a result or not.

job match {
  case FooJob(id, userId, foo) => println(s"FooJob with result $foo")
  case j: JobWithResult[_] => println(s"Other job with id ${j.id} with result")
  case j: Job => println(s"Job with id {$j.id} without result")
}

This assumes that the result is not actually optional.

CodePudding user response:

As pointed out in the comments, the Option is "unnecessary".

I submit, it's not so much "unnecessary" as indicative ... of a way for you to implement what you want without repetitive declarations.

  case class Job[Result](id: String, userId: String, result: Option[Result] = None)

  object Job { 
      def void(id: String, userId: String) = Job[Nothing](id, userId)
      def apply[R](id: String, userId: String, result: R) = Job(id, userId, Option(result))
   }

   type FooJob = Job[FooResult]
   type VoidJob = Job[Nothing]
  • Related