7

Section 4.6.2 of the Scala Language Specification Version 2.8 describes repeated parameters and says:

The last value parameter of a parameter section may be suffixed by “*”, e.g. (..., x:T*). The type of such a repeated parameter inside the method is then the sequence type scala.Seq[T].

However, this code:

abstract class A { def aSeq : Seq[A] }
class B(val aSeq : A*) extends A
class C extends B { override val aSeq :Seq[A] = Seq() }

give an error when compiled:

overriding value aSeq in class B of type A*;  value aSeq has incompatible type

The compiler seems to indicate that A* is a distinct type from Seq[A].

Investigating the actual class of aSeq in this case shows it to be an instance of scala.collection.mutable.WrappedArray$ofRef but even the following code fails to compile with the same message:

class C extends B { override val aSeq  = new ofRef(Array[A]()) }

So the question is, how do I go about overriding a member defined by a repeated parameter on the class?

In case you're wondering where this is coming from, that is exacly what scala.xml.Elem does to override the child method in scala.xml.Node.

Paul Ruane
  • 37,459
  • 12
  • 63
  • 82
DougC
  • 950
  • 7
  • 18
  • I would suggest not using repeated parameters as `val`s in a class (I am a bit surprised it compiles at all). Instead have `class B(val aSeq: Seq[A]) extends A` and if you want to have syntax `B(a1, a2, ...)` available, add it in the companion object: `object B {def apply(aSeq : A*) = new B(aSeq)}`. – Alexey Romanov Apr 01 '11 at 13:29
  • It's not my code that's using it. As I said at the end of the post, the repeated param is used by the scala.xml.Elem subclass of scala.xml.Node. I don't have the option of changing it. – DougC Apr 01 '11 at 13:40
  • Ah, I misread it. Just checked `scala.xml.Node` and not `scala.xml.Elem`. – Alexey Romanov Apr 01 '11 at 13:46
  • I tried `override val seq: Int* = Seq(1,2)` and `override val seq = Seq(1,2): _*`. Unfortunately, neither works. I suspect it may be impossible, but hopefully this is wrong. – Alexey Romanov Apr 01 '11 at 13:49

3 Answers3

5

Your issue can be summarized as:

scala> class A { def aSeq(i: Int*) = 1 }
defined class A

scala> class B extends A { override def aSeq(i: Seq[Int]) = 2 }
<console>:6: error: method aSeq overrides nothing
       class B extends A { override def aSeq(i: Seq[Int]) = 2 }

The methods have different types. The spec says (emphasis mine):

The type of such a repeated parameter inside the method is then the sequence type scala.Seq[T]

As Int* and Seq[Int] aren't inside the method, this particular sentence does not apply.

Interestingly, this following code shows that the methods have different types before erasure but the same after:

scala> class G { def aSeq(i:Int*) = 1; def aSeq(i:Seq[Int]) = 2 }
<console>:5: error: double definition:
method aSeq:(i: Seq[Int])Int and
method aSeq:(i: Int*)Int at line 5
have same type after erasure: (i: Seq)Int
       class G { def aSeq(i:Int*) = 1; def aSeq(i:Seq[Int]) = 2 }

So the question then becomes, why your B class can extend your A abstract class. There may be an inconsistency in the spec there. I don't know...

Edit: I re-read the spec and I can't figure out if there is anything related to repeated parameters and overriding. There does not seem to be anything about return type of repeated parameters, which is what you get for the val aSeq accessor method.

I think Mark's answer is a perfectly valid approach. In case you can't follow it, you can use the following workaround:

class C extends B {
  private def aSeqHelper(a: A*) = a
  override val aSeq = aSeqHelper(Seq[A](): _*)
}

So for instance:

import scala.xml._
class ElemX extends Elem("pref", "label", <xml a="b"/>.attributes, TopScope) {
  private def childHelper(c: Node*) = c
  override val child = childHelper(<foo/><bar/>: _*) }

Then:

scala> new ElemX
res4: ElemX = <pref:label a="b"><foo></foo><bar></bar></pref:label>
huynhjl
  • 41,520
  • 14
  • 105
  • 158
  • I can't find anything in the language spec that even refers to using repeated params in the class parameters, let alone what the resulting member's type is when the param is a val. – DougC Apr 01 '11 at 13:44
  • The simplest explanation for why it could override is that the compiler might consider `A*` to be a subtype of `Seq[A]` (but one which can't be used outside parameter lists). – Alexey Romanov Apr 01 '11 at 13:51
  • It certainly seems as if the compiler is treating it as an entirely distinct type. – DougC Apr 01 '11 at 13:54
  • That's done the trick! Still not sure what the real type of childHelper is, but that's an elegant hack to make it work. Thanks very much. The solution in Mark's comment is also good, but I can't use it in this case because I need the value of child to be calculated. – DougC Apr 02 '11 at 10:55
  • Practically and according to the REPL the type of `childHelper` is `(Node*) => Node*`. The return type cannot be expressed with the syntax but the compiler is happy to infer it. – huynhjl Apr 02 '11 at 11:25
4

The copy method of xml.Elem uses it like this

def copy(
  prefix: String = this.prefix,
  label: String = this.label,
  attributes: MetaData = this.attributes,
  scope: NamespaceBinding = this.scope,
  child: Seq[Node] = this.child.toSeq
): Elem = Elem(prefix, label, attributes, scope, child: _*)

So you can override the value in the B constructor

class C extends B(aSeq = Seq(): _*)

Or declare it as a parameter of the class C

class C(seq: Seq[A]) extends B(aSeq = seq: _*)

Though I am not sure it answers your question!

Mark Jayxcela
  • 985
  • 1
  • 7
  • 17
  • That doesn't quite do what I need. I need to be able to override the aSeq member from B in C, rather than just initialise it's value at construction. – DougC Apr 02 '11 at 11:02
3

The spec never allowed for repeated types to leak out in this way. The compiler was changed in July 2011 to enforce this.

For more background, check out the comments in the ticket:

https://issues.scala-lang.org/browse/SI-4176

retronym
  • 54,768
  • 12
  • 155
  • 168