1

Suppose I have the following code. Lets say alternative constructors are provided for syntactic sugar (assume this is a requirement):

object Example extends App {
  case class Doggy(name: Option[String] = None, age: Option[Int] = None)

  object Doggy {
    def apply(name: String): Doggy = Doggy(name = Some(name))
    def apply(age: Int): Doggy = Doggy(age = Some(age))
    def apply(name: String, age: Int): Doggy = Doggy(name = Some(name), age = Some(age))
  }


  val rex = ("rex", 10)
  val pex = ("pex")
  val tex = (100)
}

As you can see, the alternate constructor provides syntactic sugar allowing the user to skip writing Some(name) or Some(age) and so on.

Now in the use cases for this case class, all possible combinations of the constructor may be utilised by the user.

For a case class with many different options, constructing alternate constructors by defining apply() quickly becomes unwieldy.

Is there a way to somehow generate all combinations of the apply() programmatically?

For example, here is a stack overflow question where lists are passed to the constructor:Link. Could we generate all combinations of a list, and define an apply() on each combination?

I realise this may not be possible, but any guidance would be appreciated (perhaps even ways to improve the code pattern provided)

EDIT:

I currently just define the following in the companion object:

implicit def toOption[T](x: T): Option[T] = Option[T](x)
finite_diffidence
  • 893
  • 2
  • 11
  • 20

2 Answers2

3

You could use parameters with default values for your companion object's apply method, but as @Luis Miguel Mejia Suarez said, using Some each time would be better (and maybe even easier).

case class Doggy(name: Option[String], age: Option[Int])

object Doggy {
  def apply(name: String = null, age: Int = -1): Doggy =
    Doggy(Option(name), if (age == -1) None else Some(age))
}

You can then use it like this:

val rex = Doggy(name = "rex", age = 10)
val pex = Doggy(name = "pex")
val tex = Doggy(age = 100)

Honestly, though, I'd just use case class Doggy(name: Option[String], age: Option[Int]) and nothing more. I personally don't like using default parameters or implicit conversions, because when I do, things explode.

user
  • 7,435
  • 3
  • 14
  • 44
  • 1
    Thanks @user appreciate the feedback. The issue is I have been told to clean up the user api to exclude "Some". Personally I would stick with "Some" but due to some constraints I am looking for alternatives I may not be aware of. Thanks for this. – finite_diffidence Jul 04 '20 at 17:16
2

You can leverage copy on a singleton created with no args constructor. Suppose Doggy has a no-args constructor, you can create an instance in an object and use copy to get other instances with different arguments:

object Doggy {

  val instance = Doggy()

  def main(args: Array[String]): Unit = {
    val doggy1 = instance.copy(name = "Name")
    val doggy1 = instance.copy(age = 1)
    val doggy1 = instance.copy(name = "Name", age = 1)
    val doggy1 = instance.copy(age = 1, name = "Name")
  }

}

Note that I have omitted Option for readability.

Ad you can see, you can provide any arguments in any order to the copy method.

Andronicus
  • 25,419
  • 17
  • 47
  • 88