Search code examples
scalacase-classcompanion-object

Scala case class implementation of smart constructors?


Trying to implement smart constructor using case classes. Managed to override the copy method fine, and I presume the apply in the companion object should have done the trick, but I hit a wall when trying to pass in a BigInt. I tried putting in def apply(value: BigInt): Option[Natural] but then scalac complains about conflicting symbols.

import spire.math.Integral // companion object contains implicit Integral[BigInt]

case class Natural private (value: BigInt) {
  def copy(value: BigInt = this.value): Option[Natural] =
    Natural.apply(value)
}

object Natural {
  def apply[A](x: A)(implicit A: Integral[A]): Option[Natural] =
    if (A.isPositive(x)) Some(Natural(x))
    else None
}

/** In Foo.scala */
Natural(5L) // Option[Natural] = Some(Natural(5))
Natural(BigInt(5L)) // constructor Natural in class Natural cannot be accessed in object Foo

Perhaps such a thing is not possible?


Solution

  • Overloading is your problem here, as @jroesch points out in his answer.

    A solution to this problem is to change the type of the argument of the private primary constructor so that the latter cannot conflict with the public factory method. There are various ways of doing that in Scala ... one might go like this,

    case class Wrap[T](t: T) extends AnyVal
    
    case class Natural private (value: Wrap[BigInt]) {
      def copy(value: Wrap[BigInt] = this.value): Option[Natural] =
        Natural(value.unwrap)
    }
    
    object Natural {
      def apply[A](x: A)(implicit A: Integral[A]): Option[Natural] =
        if (A.isPositive(x)) Some(Natural(Wrap(x)))
        else None
    }