adelbertc
adelbertc

Reputation: 7320

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?

Upvotes: 1

Views: 2494

Answers (2)

Miles Sabin
Miles Sabin

Reputation: 23046

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
}

Upvotes: 5

jroesch
jroesch

Reputation: 1260

I believe that the error you are encountering is because of method overloading. When creating a case class the compiler will generate a signature of:

def apply(x: T): T 

for a case class T in it's companion object T. So when you are invoking the method, method selection picks the most specific signature first. It attempts to invoke Natural.apply(x: T): T which I would hazard is also marked private transitively in the companion object. The conflicting symbols then becomes an issue, because it generates an apply(x: T): T, and you are also defining an apply(x: T): T.

Your best bet is using a plain Scala class, and manually implementing the extractor pattern, hash, equality, ect.

Upvotes: 2

Related Questions