Idiomatic Scala way to deal with base vs derived class field names? - scala

Consider the following base and derived classes in Scala:
abstract class Base( val x : String )
final class Derived( x : String ) extends Base( "Base's " + x )
{
override def toString = x
}
Here, the identifier 'x' of the Derived class parameter overrides the field of the Base class, so invoking toString like this:
println( new Derived( "string" ).toString )
returns the Derived value and gives the result "string".
So a reference to the 'x' parameter prompts the compiler to automatically generate a field on Derived, which is served up in the call to toString. This is very convenient usually, but leads to a replication of the field (I'm now storing the field on both Base and Derived), which may be undesirable. To avoid this replication, I can rename the Derived class parameter from 'x' to something else, like '_x':
abstract class Base( val x : String )
final class Derived( _x : String ) extends Base( "Base's " + _x )
{
override def toString = x
}
Now a call to toString returns "Base's string", which is what I want. Unfortunately, the code now looks somewhat ugly, and using named parameters to initialize the class also becomes less elegant:
new Derived( _x = "string" )
There is also a risk of forgetting to give the derived classes' initialization parameters different names and inadvertently referring to the wrong field (undesirable since the Base class might actually hold a different value).
Is there a better way?
Edit 1: To clarify, I really only want the Base values; the Derived ones just appear to be necessary for initializing the fields of the base class. The example only references them to illustrate the ensuing issues.
Edit 2: Actually, the example would have been clearer if I had used vars instead of vals, since that highlights the problem with values getting changed later on in the base class:
class Base( var x : Int ) { def increment() { x = x + 1 } }
class Derived( x : Int ) extends Base( x ) { override def toString = x.toString }
val derived = new Derived( 1 )
println( derived.toString ) // yields '1', as expected
derived.increment()
println( derived.toString ) // still '1', probably unexpected
Edit 3: It might be nice to have a way to suppress automatic field generation if the derived class would otherwise end up hiding a base class field. It would appear that the Scala compiler could actually have been designed to do this for you, but of course this contradicts the more general rule of "nearer" identifiers (the Derived class' 'x') hiding more remote ones (the Base class' 'x'). It seems like a reasonably nice solution would be a modifier like 'noval', maybe like this:
class Base( var x : Int ) { def increment() { x = x + 1 } }
class Derived( noval x : Int ) extends Base( x ) { override def toString = x.toString }
val derived = new Derived( 1 )
println( derived.toString ) // yields '1', as expected
derived.increment()
println( derived.toString ) // still '2', as expected

The idiomatic way to avoid duplicating the field would be to write
abstract class Base { val x: String }
final class Derived(val x: String) extends Base {
def toString = x
}
However, in your version it looks like you actually want a second field, since you have two distinct values. As you correctly point out, giving these fields the same name is likely to lead to confusion.
Since you don't actually need the constructor argument outside of the constructor, you could use this approach (a private constructor with a companion module that acts as a factory):
abstract class Base { val x: String }
final class Derived private (val x: String) extends Base {
def toString = x
}
object Derived {
def apply(x: String) = new Derived("Base " + x)
}

As the base class is abstract, it doesn't look as though you really want a val (or var) in the Base class, with the associated backing field. Instead, you're simply looking to make a guarantee that such a thing will be available in concrete subclasses.
In Java, you'd use an accessor method such as getX to achieve this.
In Scala, we can go one better, vals, vars and defs occupy the same namespace, so a val can be used to implement an abstract def (or to override a concrete def, if that's what floats your boat). More formally, this is known as the "Uniform Access Principle"
abstract class Base{ def x: String }
class Derived(val x: String) extends Base {
override def toString = x
}
If you need for x to be settable via a reference to Base, you also need to declare the "setter" (which can then be implemented with a var):
abstract class Base {
def x: String
def x_=(s: String): Unit
}
class Derived(var x: String) extends Base {
override def toString = x
}
(not that I would ever encourage mutability in any design; unless there was an especially compelling justification for it. There are too many good reasons for favouring immutability by default)
UPDATE
The benefits of this approach are:
x could be an entirely synthetic value, implemented entirely in terms of other values
(i.e. the area of a circle for which you already know the radius)
x can be implemented at any arbitrary depth in the type hierarchy, and doesn't have to
be explicitly passed through each intervening constructor
(having a different name each time)
There's only a single backing field required, so no memory is wasted
As it now doesn't need a constructor, Base could be implemented as a trait;
if you so desire

You can try this:
abstract class Base( val x : String )
final class Derived( _x : String ) extends Base( _x ) {
override val x = "Base's " + _x
override def toString = x
}
Then
println(new Derived("string").toString)
prints exactly what you want

you already provided the answer that works
abstract class Base( val x : String )
final class Derived( _x : String ) extends Base( "Base's " + _x )
{
override def toString = x
}
If the problem is that _x is not a nice name, then you should use a meaningful one.
Alternatively, you could declare your classes as follows
abstract class Base( val _x : String )
final class Derived( x : String ) extends Base( "Base's " + x )
{
override def toString = _x
}
And now you would have the "nice" syntax for initializing Derived instances.
If scala was to allow
a way to suppress automatic field generation if the derived class would otherwise end up hiding a base class field.
This to me seems a very low-level detail that you don't want to deal with in the code. If this can be done safely, the compiler should do it for you.

As suggested by #jsuereth I created an enhancement ticked for Scala, just for the record, that I hope correctly summarizes the content of the discussions here. Thanks for all of your input! The ticket can be found here, the content below: https://issues.scala-lang.org/browse/SI-4762
Inadvertent shadowing of base class fields in derived classes, Warning desirable
Issue arises whenever (a) a class parameter in a derived class uses the same
symbol as a field or function in a base class and (b) that base class symbol
is subsequently accessed in the derived class. The derived class parameter
causes the compiler to auto-generate a field of the same name that shadows
the base class symbol. Any reference to that symbol in the Derived class,
intended to refer to the base class field, then inadvertently (a) causes
duplicate definition of the field and (b) unexpectedly (but correctly) refers
to the auto-generated field in the derived class.
Code example:
class Base( val x : String )
class Derived( x : String ) extends Base( x ) { override def toString = x }
Since the 'Base' class has 'val' to generate the field and the derived class
does not, the developer clearly intends to use the 'Derived' 'x' only for
pass-through to Base, and therefore expects the reference in 'toString' to
yield the Base value. Instead, the reference in 'toString' causes the compiler
to automatically generate a field 'Derived.x' that shadows the 'Base.x' field,
resulting in an error. The compiler behaves correctly, but the result is a
programming error.
Usage scenario (with var fields for clearer illustration of the risks):
class Base( var x : Int ) { def increment() { x = x + 1 } }
class Derived( x : Int ) extends Base( x ) { override def toString = x.toString }
val derived = new Derived( 1 )
println( derived.toString ) // yields '1', as expected
derived.increment()
println( derived.toString ) // still '1', probably unexpected
Since this issue arises whenever anyone uses the default way to initialize
base class fields from a derived class in Scala, the scenario would appear to
be extremely common and result in lots of programming errors (easily fixed,
but still) for newer users.
An easy work-around for this issue exists (use differing names for the derived
class parameter, like '_x', 'theX', 'initialX', etc.), but this introduces
unwanted extra symbols.
Solution A (Minimal): issue a warning whenever the compiler infers that a
class parameter requires an auto-generated field in a derived class that
would shadow a symbol already defined in a base class.
Solution B: the work-around, still required with Solution A, is to come up
with a new symbol name each time one initializes a base class field. This
scenario comes up all the time and polluting the namespace with workaround
field names like '_x' and 'theX' seems undesirable. Instead, it might be nice
to have a way to suppress automatic field generation if the developer
determines that the derived class symbols would otherwise end up hiding a base
class symbol (e.g., following the warning of Solution A). Maybe a useful
addition to Scala would be a modifier like 'noval' (or 'passthrough' or
'temp', or whatever - in addition to 'val' and 'var'), like this:
class Base( var x : Int ) { def increment() { x = x + 1 } }
class Derived( noval x : Int ) extends Base( x ) { override def toString = x.toString }
val derived = new Derived( 1 )
println( derived.toString ) // yields '1', as expected
derived.increment()
println( derived.toString ) // still '2', as expected

Ok, first of all I would like to point out that the answer from #Yuriy Zubarev is probably what you really want to have. Secondly, I think the problem might lie in your design. Check this out. This is the part of your code:
extends Base( "Base's " + _x )
So some value x comes into your derived class and gets modified with the information (in this case "Base's " + ...). Do you see the problem? Why does your derived type knows something that your base type actually should have known? Here is the solution I propose.
abstract class Base {
// this works especially well if you have a var
// which is what you wanna have as you pointed out later.
var x: String
x = "Base's " + x
}
final class Derived(override var x: String ) extends Base{
override def toString = x
}
This might sound harsh, but if this solution helps you out it automatically means that you had bad design. If on the other hand it does not help, than I probably don't understand you problem correctly and therefore apologize right away.

#Gregor Scheidt
your code does not work if I move toString() down to Derived, as the following:
object Test {
abstract class Base ( val x: String)
final class Derived(x: String) extends Base(x + " base") {
override def toString() = x
}
def main(args: Array[String]): Unit = {
val d = new Derived( "hello")
println( d) // hello
}
}
A post from the official site said,
A parameter such as class Foo(x : Int) is turned into a field if it is
referenced in one or more methods
And Martin's reply confirms its truth:
That's all true, but it should be treated as an implementation
technique. That's why the spec is silent about it.
since there is no way to prevent the compiler's action, my option is that a more robust way to reference base class field is to use a different name, for example using a underscore "_" as prefix, as the following:
object Test {
abstract class Base ( val x: String)
final class Derived(_x: String) extends Base(_x + " base") {
override def toString() = x
}
def main(args: Array[String]): Unit = {
val d = new Derived( "hello")
println( d) // hello
}
}

Related

Is this class Immutable or mutable

Is the below Scala class is mutable or immutable?
I believe that its immutable as I can't edit the variables or access them once its created but whats making me doubt myself is the fact that it returns the current instance of a variable using its functions. It also does not have final in front of it which is further making me doubt myself.
class person(name:String, dob:String){
def getName = name
def getDob = dob
def display() = {
println("Name "+name+" dob: "+dob)
}
}
Thanks,
You have a misconception with the term Immutable:
I believe that its immutable as I can't edit the variables or access
them once its created
That's the definition of a private thing (method, variable, ...). Immutability refers to the fact that you cannot mutate state, that is, you can't change the value of something unless you create a new instance of it.
Let's see it with an example:
trait Foo{
def myMutableValue: Int
}
class Clazz extends Foo{
var myMutableValue = 1
def changeState(): Int = {
myMutableValue += 1
myMutableValue
}
}
val bar = new Clazz
bar.changeState() // myMutableValue = 2
bar.changeState() // myMutableValue = 3
bar.changeState() // myMutableValue = 4
bar.myMutableValue // myMutableValue = 4
With that example, in your instance of Clazz (bar) you're changing the state of a class attribute, in this case myMutableValue is changing its value every time I invoke changeState.
Please note that the class is public by default and changeState is also public and that doesn't means that is immutable.
Now, let's see an immutable approach:
trait Foo{
def myMutableValue: Int
}
class Clazz extends Foo{
val myMutableValue = 1
def changeState(): Int = myMutableValue + 1
}
val instance = new Clazz
instance.changeState() // myMutableValue = 2
instance.changeState() // myMutableValue = 2
instance.changeState() // myMutableValue = 2
instance.myMutableValue // 1
With this approach, every call to changeState will evaluate to 2, no matter how many times I call the function. That is, because we're dealing with an immutable value (val myMutableValue = 1). Every invocation of changeState will perform the evaluation and return a copy of that value. You're not modifying in any way the value of myMutableValue.
Please take a look to this and this.
Also, please take a look at your code, you have some errors:
By convention, class name should be capitalized (Person instead of person).
You don't need to reassign your class values with def (def getNameand def getDob). You can use class values as is.
Lastly:
It also does not have final in front of it which is further making me
doubt myself.
Again, you're talking about different things. final, as in Java, is a modifier to prevent your class to be extended. It doesn't relate in any way to immutability In adition, if you want to prevent mutability in your subclass you have to make all their members final (see this).
Since your example is coded in Scala you have all the tools that the language itself offers at your disposal (e.g. val, sealed, final)
Please note that I've used a trait to explain the possible use of def.
EDIT: about final modifier and immutability
Thanks to #Silvio Mayolo and #puhlen for the comments and clarification about final

Enum.Value vs Enum#Value

I'm learning Scala coming from a Java background, and the first thing I've found that works significantly differently than Java are the Enums. I've managed to accomplish everything I've wanted to just by trial and error, but I'd love to better understand what I'm doing along the way.
From the Scala documentation, I'm told to create an enum by extending the class Enumeration, and add values by setting them equal to a constant Value, eg:
object Label extends Enumeration{
val NONE = Value
}
This works about as expected. My trouble comes in using not only enums but extensions of custom written enum extensions. I wrote a chunk of code as part of a Machine Learning class (now over) to separate data by their labels (for use in TDIDT, for example). At the bottom is a small piece of it to get at where I'm confused. The Data object is runnable, just to try it out.
First, on the print statement, I thought it would be true, but it is not
println(Label.NONE.equals(MessageLabel.NONE))//Thought this would be true, is false
Why is this the case? Is that even though the NONE that MessageLabel has inherited is directly from Label, the type system insists that they are different enum values?
Secondly and more importantly I've been going back and forth between Label.Value and Label#Value basically willy-nilly. The version that I posted with:
def splitByLabel[T <: Label#Value]
trait Labelable[T <: Label#Value]
abstract class Data[T <: Label#Value]
class Message( ... val label : MessageLabel.Value)
Compiles and runs correctly. When I change all the #s to ., I get a compile time error on the line splitByLabel(messages).foreach(a => println(a)), stating:
Inferred type arguments [MessageLabel.Value] do not conform to method splitByLabel's type parameter bounds[T <: Label.Value]
But when I change all the .s to #s, I get a compile time error on the line class Message(val index : Int, val s : Map[Double, Int], override val label : MessageLabel#Value) extends Data[MessageLabel#Value](label), stating:
Not Found: Type MessageLabel
So clearly there is a difference between the two and they each fill a specific role. Can someone help me understand what the difference is? Thank you!
/** Enum type all labels should extend. Guarantees access of universal NONE label */
class Label extends Enumeration{
val NONE = Value
}
/** Singleton instance for accessing NONE */
object Label extends Label{}
/** Companion object to all data classes. Hosts helper methods and a runnable main method */
object Data{
/** Returns a map of lists, each list is similarly labeled data. Map is label -> list of data */
def splitByLabel[T <: Label#Value](elms : List[Labelable[T]]) : Map[T, List[Labelable[T]]] = {
def f(acc : Map[T, List[Labelable[T]]], e : Labelable[T]) : Map[T, List[Labelable[T]]] = {
if(acc.contains(e.label)){
val l = acc(e.label)
acc - e.label + ((e.label, (e :: l)))
} else{
acc + ((e.label, List(e)))
}
}
elms.foldLeft(Map[T, List[Labelable[T]]]())(f)
}
def main(args : Array[String]){
println(Label.NONE.equals(MessageLabel.NONE))
val messages : List[Message] = (0 to 10).toList.map(a =>
new Message(a, Map(), if(a % 3 == 0) MessageLabel.HAM else MessageLabel.SPAM))
splitByLabel(messages).foreach(a => println(a))
}
}
/** Implementing classes can be labeled */
trait Labelable[T <: Label#Value]{
/** Returns the label of this thing */
val label : T
/** The possible labelings for this thing */
val labels : List[T]
}
abstract class Data[T <: Label#Value](override val label : T) extends Labelable[T]{
override def toString(): String = {
if (label != null)
label.toString
else
"NO_LABEL"
}
}
object MessageLabel extends Label{
val HAM, SPAM = Value
}
/** An instance represents a sentence. */
class Message(val index : Int, val s : Map[Int, Double], override val label : MessageLabel.Value)
extends Data[MessageLabel.Value](label){
/** Returns the possible labelings for a message */
override val labels = MessageLabel.values.toList
/** Adds index to tostring at front */
override def toString() : String = {
index + "-" + super.toString
}
}
Label#Value is the type Value in the type Label. Label.Value is the type Value in the value Label. (It's a bit confusing because you have both class Label and object Label (i.e. a value)). So a MessageLabel.Value is a Label#Value, because MessageLabel is an instance of the type (class) Label. But it isn't a Label.Value, because MessageLabel isn't the value (object) Label. And there is no MessageLabel#Value because there is no class MessageLabel (or trait).
(FWIW I find scala Enumeration very confusing and prefer to use Java enums in my Scala code)
This is not particular to Enumeration.
scala> class A { class B ; val None = new B }
defined class A
scala> class C extends A ; class D extends A
defined class C
defined class D
scala> val c = new C ; val d = new D
c: C = C#45fe3ee3
d: D = D#4cdf35a9
scala> c.None == d.None
res0: Boolean = false
No one would ever expect that to be true. One value is initialized in one (super-) constructor, another in the other.
Also, Value is not a constant; it's a function that says, "Give me another Value." So you're generating a value for each instance.
In Java, you can't extend enums in this sense. To "extend" is to add members or increase the extension, but subclassing means a subset or restricted domain.
This is a case where one prefers composition over inheritance. Given a set of weekdays and of weekend days, I get alldays by adding them, not by extending weekdays with the weekend.
Here is an example of using an Enumeration in a path-dependent way.
Another issue with the code as it stands:
scala> MessageLabel.NONE
res4: MessageLabel.Value = <Invalid enum: no field for #0>
https://issues.scala-lang.org/browse/SI-5147

Accessibility of primary constructor parameters in scala

I am having hard time to understand the concept of primary constructor and it's parameters. What I have understood till now is: if we define a class as following
class Example(a: Int, b: Int)
Scala compiler generates a primary constructor of the class Examples with the above two parameters. But, it doesn't defines fields a and b in the class Example's definition. But if we define
class Example(val a: Int, val b: Int)
scala compiler generates the primary constructor as above and adds two fields in the class definition.
Now the problem comes when I am trying an example like
class PrimaryConstructor(a: Int, b: Int){
override def toString() = "PrimaryConstructor(" + this.a + ", " + this.b + ")"
}
The above code compiles well even if there is no fields named either a or b. I am not able to understand that if there are no any fields as such then how I am able to access them using this: the current object reference.
object Main{
def main(args: Array[String]){
val primaryConstructor = new PrimaryConstructor(1, 2)
println(primaryConstructor.a)
}
}
While if I try to access them from out side the class definition as above, I get the following error message after compilation.
error: value a is not a member of PrimaryConstructor
println(primaryConstructor.a)
I can understand this. But, how can I access those fields using this? Please help me to understand this.
It basically generates a private val, so
class A(a:Int) {
def func = a
}
and
class A(private[this] val a:Int) {
def func = a
}
are equivalent. This may not be entirely true if you omit the function.
When a constructor parameter is referred outside the constructor body ( such as in example func above ), Scala generates a private[this] val, otherwise not.
You can check scala spec for more details or look at this stackoverflow question
Martin's answer is great:
It basically generates a private val, so
class A(a:Int) {
def func = a
}
and
class A(private[this] val a:Int) {
def func = a
}
are equivalent and you can access a from inside your class.
But, note that class A(a: Int) means that the field a is instance private. Meaning that you cannot write something like this:
class A(a: Int){
def sum(other: A): Int = {
this.a + other.a
}
}
other.a is not allowed even though both instances are of the same type. You can only use this.a.

How to create a Scala class with private field with public getter, and primary constructor taking a parameter of the same name

Search results so far have led me to believe this is impossible without either a non-primary constructor
class Foo { // NOT OK: 2 extra lines--doesn't leverage Scala's conciseness
private var _x = 0
def this(x: Int) { this(); _x = x }
def x = _x
}
val f = new Foo(x = 123) // OK: named parameter is 'x'
or sacrificing the name of the parameter in the primary constructor (making calls using named parameters ugly)
class Foo(private var _x: Int) { // OK: concise
def x = _x
}
val f = new Foo(_x = 123) // NOT OK: named parameter should be 'x' not '_x'
ideally, one could do something like this:
class Foo(private var x: Int) { // OK: concise
// make just the getter public
public x
}
val f = new Foo(x = 123) // OK: named parameter is 'x'
I know named parameters are a new thing in the Java world, so it's probably not that important to most, but coming from a language where named parameters are more popular (Python), this issue immediately pops up.
So my question is: is this possible? (probably not), and if not, why is such an (in my opinion) important use case left uncovered by the language design? By that, I mean that the code either has to sacrifice clean naming or concise definitions, which is a hallmark of Scala.
P.S. Consider the case where a public field needs suddenly to be made private, while keeping the getter public, in which case the developer has to change 1 line and add 3 lines to achieve the effect while keeping the interface identical:
class Foo(var x: Int) {} // no boilerplate
->
class Foo { // lots of boilerplate
private var _x: Int = 0
def this(x: Int) { this(); _x = x }
def x = _x
}
Whether this is indeed a design flaw is rather debatable. One would consider that complicating the syntax to allow this particular use case is not worthwhile.
Also, Scala is after all a predominantly functional language, so the presence of vars in your program should not be that frequent, again raising the question if this particular use case needs to be handled in a special way.
However, it seems that a simple solution to your problem would be to use an apply method in the companion object:
class Foo private(private var _x: Int) {
def x = _x
}
object Foo {
def apply(x: Int): Foo = new Foo(x)
}
Usage:
val f = Foo(x = 3)
println(f.x)
LATER EDIT:
Here is a solution similar to what you originally requested, but that changes the naming a bit:
class Foo(initialX: Int) {
private var _x = initialX
def x = _x
}
Usage:
val f = new Foo(initialX = 3)
The concept you are trying to express, which is an object whose state is mutable from within the object and yet immutable from the perspective of other objects ... that would probably be expressed as an Akka actor within the context of an actor system. Outside the context of an actor system, it would seem to be a Java conception of what it means to be an object, transplanted to Scala.
import akka.actor.Actor
class Foo(var x: Int) extends Actor {
import Foo._
def receive = {
case WhatIsX => sender ! x
}
}
object Foo {
object WhatIsX
}
Not sure about earlier versions, but In Scala 3 it can easily be implemented like follows:
// class with no argument constructor
class Foo {
// prive field
private var _x: Int = 0
// public getter
def x: Int = _x
// public setter
def x_=(newValue: Int): Unit =
_x = newValue
//auxiliary constructor
def this(value: Int) =
this()
_x = value
}
Note
Any definition within the primary constructor makes the definition public, unless you prepend it with private modifier
Append _= after a method name with Unit return type to make it a setter
Prepending a constructor parameter neither with val nor with var, makes it private
Then it follows:
val noArgFoo = Foo() // no argument case
println(noArgFoo.x) // the public getter prints 0
val withArgFoo = Foo(5) // with argument case
println(withArgFoo.x) // the public getter prints 5
noArgFoo.x = 100 // use the public setter to update x value
println(noArgFoo.x) // the public getter prints 100
withArgFoo.x = 1000 // use the public setter to update x value
println(withArgFoo.x) // the public getter prints 1000
This solution is exactly what you asked; in a principled way and without any ad hoc workaround e.g. using companion objects and the apply method.

case class copy 'method' with superclass

I want to do something like this:
sealed abstract class Base(val myparam:String)
case class Foo(override val myparam:String) extends Base(myparam)
case class Bar(override val myparam:String) extends Base(myparam)
def getIt( a:Base ) = a.copy(myparam="changed")
I can't, because in the context of getIt, I haven't told the compiler that every Base has a 'copy' method, but copy isn't really a method either so I don't think there's a trait or abstract method I can put in Base to make this work properly. Or, is there?
If I try to define Base as abstract class Base{ def copy(myparam:String):Base }, then case class Foo(myparam:String) extends Base results in class Foo needs to be abstract, since method copy in class Base of type (myparam: String)Base is not defined
Is there some other way to tell the compiler that all Base classes will be case classes in their implementation? Some trait that means "has the properties of a case class"?
I could make Base be a case class, but then I get compiler warnings saying that inheritance from case classes is deprecated?
I know I can also:
def getIt(f:Base)={
(f.getClass.getConstructors.head).newInstance("yeah").asInstanceOf[Base]
}
but... that seems very ugly.
Thoughts? Is my whole approach just "wrong" ?
UPDATE I changed the base class to contain the attribute, and made the case classes use the "override" keyword. This better reflects the actual problem and makes the problem more realistic in consideration of Edmondo1984's response.
This is old answer, before the question was changed.
Strongly typed programming languages prevent what you are trying to do. Let's see why.
The idea of a method with the following signature:
def getIt( a:Base ) : Unit
Is that the body of the method will be able to access a properties visible through Base class or interface, i.e. the properties and methods defined only on the Base class/interface or its parents. During code execution, each specific instance passed to the getIt method might have a different subclass but the compile type of a will always be Base
One can reason in this way:
Ok I have a class Base, I inherit it in two case classes and I add a
property with the same name, and then I try to access the property on
the instance of Base.
A simple example shows why this is unsafe:
sealed abstract class Base
case class Foo(myparam:String) extends Base
case class Bar(myparam:String) extends Base
case class Evil(myEvilParam:String) extends Base
def getIt( a:Base ) = a.copy(myparam="changed")
In the following case, if the compiler didn't throw an error at compile time, it means the code would try to access a property that does not exist at runtime. This is not possible in strictly typed programming languages: you have traded restrictions on the code you can write for a much stronger verification of your code by the compiler, knowing that this reduces dramatically the number of bugs your code can contain
This is the new answer. It is a little long because few points are needed before getting to the conclusion
Unluckily, you can't rely on the mechanism of case classes copy to implement what you propose. The way the copy method works is simply a copy constructor which you can implement yourself in a non-case class. Let's create a case class and disassemble it in the REPL:
scala> case class MyClass(name:String, surname:String, myJob:String)
defined class MyClass
scala> :javap MyClass
Compiled from "<console>"
public class MyClass extends java.lang.Object implements scala.ScalaObject,scala.Product,scala.Serializable{
public scala.collection.Iterator productIterator();
public scala.collection.Iterator productElements();
public java.lang.String name();
public java.lang.String surname();
public java.lang.String myJob();
public MyClass copy(java.lang.String, java.lang.String, java.lang.String);
public java.lang.String copy$default$3();
public java.lang.String copy$default$2();
public java.lang.String copy$default$1();
public int hashCode();
public java.lang.String toString();
public boolean equals(java.lang.Object);
public java.lang.String productPrefix();
public int productArity();
public java.lang.Object productElement(int);
public boolean canEqual(java.lang.Object);
public MyClass(java.lang.String, java.lang.String, java.lang.String);
}
In Scala, the copy method takes three parameter and can eventually use the one from the current instance for the one you haven't specified ( the Scala language provides among its features default values for parameters in method calls)
Let's go down in our analysis and take again the code as updated:
sealed abstract class Base(val myparam:String)
case class Foo(override val myparam:String) extends Base(myparam)
case class Bar(override val myparam:String) extends Base(myparam)
def getIt( a:Base ) = a.copy(myparam="changed")
Now in order to make this compile, we would need to use in the signature of getIt(a:MyType) a MyType that respect the following contract:
Anything that has a parameter myparam and maybe other parameters which
have default value
All these methods would be suitable:
def copy(myParam:String) = null
def copy(myParam:String, myParam2:String="hello") = null
def copy(myParam:String,myParam2:Option[Option[Option[Double]]]=None) = null
There is no way to express this contract in Scala, however there are advanced techniques that can be helpful.
The first observation that we can do is that there is a strict relation between case classes and tuples in Scala. In fact case classes are somehow tuples with additional behaviour and named properties.
The second observation is that, since the number of properties of your classes hierarchy is not guaranteed to be the same, the copy method signature is not guaranteed to be the same.
In practice, supposing AnyTuple[Int] describes any Tuple of any size where the first value is of type Int, we are looking to do something like that:
def copyTupleChangingFirstElement(myParam:AnyTuple[Int], newValue:Int) = myParam.copy(_1=newValue)
This would not be to difficult if all the elements were Int. A tuple with all element of the same type is a List, and we know how to replace the first element of a List. We would need to convert any TupleX to List, replace the first element, and convert the List back to TupleX. Yes we will need to write all the converters for all the values that X might assume. Annoying but not difficult.
In our case though, not all the elements are Int. We want to treat Tuple where the elements are of different type as if they were all the same if the first element is an Int. This is called
"Abstracting over arity"
i.e. treating tuples of different size in a generic way, independently of their size. To do it, we need to convert them into a special list which supports heterogenous types, named HList
Conclusion
Case classes inheritance is deprecated for very good reason, as you can find out from multiple posts in the mailing list: http://www.scala-lang.org/node/3289
You have two strategies to deal with your problem:
If you have a limited number of fields you require to change, use an approach such as the one suggested by #Ron, which is having a copy method. If you want to do it without losing type information, I would go for generifying the base class
sealed abstract class Base[T](val param:String){
def copy(param:String):T
}
class Foo(param:String) extends Base[Foo](param){
def copy(param: String) = new Foo(param)
}
def getIt[T](a:Base[T]) : T = a.copy("hello")
scala> new Foo("Pippo")
res0: Foo = Foo#4ab8fba5
scala> getIt(res0)
res1: Foo = Foo#5b927504
scala> res1.param
res2: String = hello
If you really want to abstract over arity, a solution is to use a library developed by Miles Sabin called Shapeless. There is a question here which has been asked after a discussion : Are HLists nothing more than a convoluted way of writing tuples? but I tell you this is going to give you some headache
If the two case classes would diverge over time so that they have different fields, then the shared copy approach would cease to work.
It is better to define an abstract def withMyParam(newParam: X): Base. Even better, you can introduce an abstract type to retain the case class type upon return:
scala> trait T {
| type Sub <: T
| def myParam: String
| def withMyParam(newParam: String): Sub
| }
defined trait T
scala> case class Foo(myParam: String) extends T {
| type Sub = Foo
| override def withMyParam(newParam: String) = this.copy(myParam = newParam)
| }
defined class Foo
scala>
scala> case class Bar(myParam: String) extends T {
| type Sub = Bar
| override def withMyParam(newParam: String) = this.copy(myParam = newParam)
| }
defined class Bar
scala> Bar("hello").withMyParam("dolly")
res0: Bar = Bar(dolly)
TL;DR: I managed to declare the copy method on Base while still letting the compiler auto generate its implementations in the derived case classes. This involves a little trick (and actually I'd myself just redesign the type hierarchy) but at least it goes to show that you can indeed make it work without writing boiler plate code in any of the derived case classes.
First, and as already mentioned by ron and Edmondo1984, you'll get into troubles if your case classes have different fields.
I'll strictly stick to your example though, and assume that all your case classes have the same fields (looking at your github link, this seems to be the case of your actual code too).
Given that all your case classes have the same fields, the auto-generated copy methods will have the same signature which is a good start. It seems reasonable then to just add the common definition in Base, as you did:
abstract class Base{ def copy(myparam: String):Base }
The problem is now that scala won't generate the copy methods, because there is already one in the base class.
It turns out that there is another way to statically ensure that Base has the right copy method, and it is through structural typing and self-type annotation:
type Copyable = { def copy(myParam: String): Base }
sealed abstract class Base(val myParam: String) { this : Copyable => }
And unlike in our earlier attempt, this will not prevent scala to auto-generate the copy methods.
There is one last problem: the self-type annotation makes sure that sub-classes of Base have a copy method, but it does not make it publicly availabe on Base:
val foo: Base = Foo("hello")
foo.copy()
scala> error: value copy is not a member of Base
To work around this we can add an implicit conversion from Base to Copyable. A simple cast will do, as a Base is guaranteed to be a Copyable:
implicit def toCopyable( base: Base ): Base with Copyable = base.asInstanceOf[Base with Copyable]
Wrapping up, this gives us:
object Base {
type Copyable = { def copy(myParam: String): Base }
implicit def toCopyable( base: Base ): Base with Copyable = base.asInstanceOf[Base with Copyable]
}
sealed abstract class Base(val myParam: String) { this : Base. Copyable => }
case class Foo(override val myParam: String) extends Base( myParam )
case class Bar(override val myParam: String) extends Base( myParam )
def getIt( a:Base ) = a.copy(myParam="changed")
Bonus effect: if we try to define a case class with a different signature, we get a compile error:
case class Baz(override val myParam: String, truc: Int) extends Base( myParam )
scala> error: illegal inheritance; self-type Baz does not conform to Base's selftype Base with Base.Copyable
To finish, one warning: you should probably just revise your design to avoid having to resort to the above trick.
In your case, ron's suggestion to use a single case class with an additional etype field seems more than reasonable.
I think this is what extension methods are for. Take your pick of implementation strategies for the copy method itself.
I like here that the problem is solved in one place.
It's interesting to ask why there is no trait for caseness: it wouldn't say much about how to invoke copy, except that it can always be invoked without args, copy().
sealed trait Base { def p1: String }
case class Foo(val p1: String) extends Base
case class Bar(val p1: String, p2: String) extends Base
case class Rab(val p2: String, p1: String) extends Base
case class Baz(val p1: String)(val p3: String = p1.reverse) extends Base
object CopyCase extends App {
implicit class Copy(val b: Base) extends AnyVal {
def copy(p1: String): Base = b match {
case foo: Foo => foo.copy(p1 = p1)
case bar: Bar => bar.copy(p1 = p1)
case rab: Rab => rab.copy(p1 = p1)
case baz: Baz => baz.copy(p1 = p1)(p1.reverse)
}
//def copy(p1: String): Base = reflect invoke
//def copy(p1: String): Base = macro xcopy
}
val f = Foo("param1")
val g = f.copy(p1="param2") // normal
val h: Base = Bar("A", "B")
val j = h.copy("basic") // enhanced
println(List(f,g,h,j) mkString ", ")
val bs = List(Foo("param1"), Bar("A","B"), Rab("A","B"), Baz("param3")())
val vs = bs map (b => b copy (p1 = b.p1 * 2))
println(vs)
}
Just for fun, reflective copy:
// finger exercise in the api
def copy(p1: String): Base = {
import scala.reflect.runtime.{ currentMirror => cm }
import scala.reflect.runtime.universe._
val im = cm.reflect(b)
val ts = im.symbol.typeSignature
val copySym = ts.member(newTermName("copy")).asMethod
def element(p: Symbol): Any = (im reflectMethod ts.member(p.name).asMethod)()
val args = for (ps <- copySym.params; p <- ps) yield {
if (p.name.toString == "p1") p1 else element(p)
}
(im reflectMethod copySym)(args: _*).asInstanceOf[Base]
}
This works fine for me:
sealed abstract class Base { def copy(myparam: String): Base }
case class Foo(myparam:String) extends Base {
override def copy(x: String = myparam) = Foo(x)
}
def copyBase(x: Base) = x.copy("changed")
copyBase(Foo("abc")) //Foo(changed)
There is a very comprehensive explanation of how to do this using shapeless at http://www.cakesolutions.net/teamblogs/copying-sealed-trait-instances-a-journey-through-generic-programming-and-shapeless ; in case the link breaks, the approach uses the copySyntax utilities from shapeless, which should be sufficient to find more details.
Its an old problem, with an old solution,
https://code.google.com/p/scala-scales/wiki/VirtualConstructorPreSIP
made before the case class copy method existed.
So in reference to this problem each case class MUST be a leaf node anyway, so define the copy and a MyType / thisType plus the newThis function and you are set, each case class fixes the type. If you want to widen the tree/newThis function and use default parameters you'll have to change the name.
as an aside - I've been waiting for compiler plugin magic to improve before implementing this but type macros may be the magic juice. Search in the lists for Kevin's AutoProxy for a more detailed explanation of why my code never went anywhere