6

Let's say I have a setting such as this:

sealed trait Annotation {
    def notes : Seq[String]
}

trait Something extends Annotation{
    //do something funny
}

case class A(val i:Int)(val notes:Seq[String] = Nil) extends Something
object A{
    def apply(a:A)(notes:Seq[String] = Nil):A = A(a.i)(notes)
}

case class B(val b:Boolean)(val notes:Seq[String] = Nil) extends Something
object B{
    def apply(b:B)(notes:Seq[String] = Nil):B = B(b.b)(notes)
}

case class C(val s:String)(val notes:Seq[String] = Nil) extends Something
object C{
    def apply(c:C)(notes:Seq[String] = Nil) :C = C(c.s)(notes)
}

Trying to compile this will result in

Main.scala:10: error: in object A, multiple overloaded alternatives of method apply define
default arguments.
object A{
       ^

Main.scala:15: error: in object B, multiple overloaded alternatives of method apply define
default arguments.
object B{
       ^

Main.scala:20: error: in object C, multiple overloaded alternatives of method apply define
default arguments.
object C{
       ^
three errors found

I have read this, so I do at least have an idea as to why this is happening, what I don't know, however, is how I am supposed to resolve the issue.

One possibility would - of course - be to simply omit the default values and force the client to provide Nil when no notes are to be stored, but is there a better solution?

2
  • Yes, you are right about the linked issue. Alternatively you can create a different method than apply. e.g. def withNotes(notes:Seq[String] = Nil) Commented May 20, 2015 at 7:17
  • 2
    And if we are assuming I'm supposed to use apply? Commented May 20, 2015 at 8:04

1 Answer 1

5

My first guess was to simply make the default arguments explicit:

case class A(i: Int)(val notes: Seq[String]) extends Something
object A {
  def apply(i: Int): A = new A(i)(Nil)
  def apply(a: A)(notes: Seq[String]): A = new A(a.i)(notes)
  def apply(a: A): A = new A(a.i)(Nil)
}

However, now, because of currying, you just have a function Int => A and Int => Seq[String] => A (and analogous for A => A) with the same name in scope.


If you refrain from currying you can manually define the overloaded methods:

case class B(b: Boolean, notes: Seq[String]) extends Something

object B {
  def apply(b: Boolean): B = B(b, Nil)
  def apply(b: B, notes: Seq[String] = Nil): B = B(b.b, notes)
}

But, since notes is now part of the same parameter list as b, the behavior of the case-class methods such as toString is changed.

println(B(true))                  // B(true,List())
println(B(true, List("hello")))   // B(true,List(hello))
println(B(B(false)))              // B(false,List())

Finally, to mimic the original behavior more closely, you can implement your own equals, hashCode, toString, and unapply methods:

class C(val s:String, val notes:Seq[String] = Nil) extends Something {

  override def toString = s"C($s)"

  override def equals(o: Any) = o match {
    case C(`s`) => true
    case _ => false
  }

  override def hashCode = s.hashCode
}

object C{
  def apply(s: String, notes: Seq[String]) = new C(s, notes)
  def apply(s: String): C = C(s, Nil)
  def apply(c:C, notes:Seq[String] = Nil): C = C(c.s, notes)
  def unapply(c: C): Option[String] = Some(c.s)
}

Example:

val c1 = C("hello")
val c2 = C("hello", List("world"))
println(c1)                 // C(hello)
println(c2)                 // C(hello)
println(c1 == c2)           // true
c1 match {                  // hello
  case C(n) => println(n)
  case _ =>
}
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.