Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Overriding a repeated class parameter in Scala?

Tags:

scala

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.

like image 823
DougC Avatar asked Apr 01 '11 10:04

DougC


People also ask

How do you override variables in Scala?

In scala, you can override only those variables which are declared by using val keyword in both classes.

What is override method in Scala?

Scala overriding method provides your own implementation of it. When a class inherits from another, it may want to modify the definition for a method of the superclass or provide a new version of it. This is the concept of Scala method overriding and we use the 'override' modifier to implement this.

How do you define a method in Scala?

We can define a method's list of parameters in parentheses after its name. After the parameters list, we can provide a return type for the method with the leading colon sign. We then define a method's body after the equals sign. The compiler allows us to skip the curly braces if there is only one statement of code.

How do you define a class in Scala?

Defining a classClass names should be capitalized. The keyword new is used to create an instance of the class. We call the class like a function, as User() , to create an instance of the class. It is also possible to explicitly use the new keyword, as new User() , although that is usually left out.


3 Answers

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>
like image 136
huynhjl Avatar answered Oct 15 '22 21:10

huynhjl


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!

like image 43
Mark Jayxcela Avatar answered Oct 15 '22 22:10

Mark Jayxcela


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

like image 3
retronym Avatar answered Oct 15 '22 21:10

retronym