Constructor arguments are visible in entire class -- a feature or a bug? - scala

Consider such code (this is just example not real code):
class Foo(url : String) extends Bar(url)
{
def say() { println(url) }
}
It compiles and it works. With nonsense results "of course". I am too newbie to judge, but for me it serves no purpose but confusion -- by definition it is impossible that the argument of the constructor could be reachable directly in another method.
Could someone more experience in Scala could point out condition when it might work or make sense? Or confirm my suspicion this is flaw in current Scala compiler.
Update
class Bar(val Url : String)
{
}
Why "nonsense". Because my will (no "var" and no "val" in Foo) was to just pass the argument, nothing else. So when I actually use the constructor argument it is just matter of what entity I use by mistake. When you write on purpose, you hit the jackpot each time, but if you don't (i.e. you make a mistake in spelling), you use entities by random. It is not the code does not make sense, it is the computation just does not make sense, I could roll a dice as well. There is a method scope, and I don't see a reason why there shouldn't be constructor scope.
Update -- workaround
So it seems this evil construct is really part of the language (by design). As UserUnknown and Tomasz Nurkiewicz suggested the only line of defense against making stupid typo is convention, however lower-upper case is not good. "A" and "a" differ a lot, but "U" and "u" not much. Introducing one special character (as Tomasz showed) is much better, because it is possible to visually detect fishy usage of constructor argument.
I will use "$" for "just-passing" constructor arguments, it is harder to type for me, and you don't see this character too often in the code.
Thank you for the answers!
Why it is evil? Because implicit actions should be allowed explicitly by users -- good examples are "dynamic" in C#, implicit conversions in Scala. And examples of breaking this rule which led to tons of problems are -- implicit conversions to bool in C++, implicit constructors in C++, declaration by usage in Perl. And this particular case is very, very close to the mentioned perlism, in Perl finally there was change in interpreter to detect such misusages, why Scala repeated the same mistake? I wonder.

Your suspicions are entirely merit-less. This is by design.
Parameters of a class are part of the class. They'll be preserved as field if necessary (such as in your example), or not if they are never used outside construction.
So, basically, if you don't need it as a field, it won't be. If you do, it will. And you'll never write a single extra character of code to tell the compiler what it can figure out by itself.

It's not a bug, it's a feature. In fact, a really nice one. Need an example how useful it is? Here is how I use it with Spring and dependency injection via constructor:
#Service
class Foo #Autowired() (bar: Bar, jdbcOperations: JdbcOperations) {
def serverTime() = bar.format(jdbcOperations.queryForObject("SELECT now()", classOf[Date]))
}
Equivalent code in Java:
#Service
public class Foo
{
private final Bar bar;
private final JdbcOperations jdbcOperations;
#Autowired
public Foo(Bar bar, JdbcOperations jdbcOperations)
{
this.bar = bar;
this.jdbcOperations = jdbcOperations;
}
public String serverTime()
{
return this.bar.format(this.jdbcOperations.queryForObject("SELECT now()", Date.class));
}
}
Still not convinced?
Short tutorial:
class Foo(var x: Int, val y: Int, z: Int) {
println(z)
//def zz = z
}
x will become a variable with getters and setter. y will become an immutable variable and z will become an immutable variable only if zz method is uncommented. Otherwise it will remain a constructor argument. Neat!
UPDATE: I see your point now! The following code works as expected by accessing url variable in base class:
class Bar(val url)
class Foo(_url : String) extends Bar(_url)
{
def say() { println(url) }
}
I agree, this is both ugly and is asking for trouble. In fact I once hit this problem myself when using Scala classes as Hibernate entities - I used constructor parameter instead of field in base class which caused duplicated field to be created: one in base class and one in derived class. I wouldn't even notice but Hibernate was screaming at runtime that duplicated column mapping was defined.
So I have to somewhat agree with you - this is somehow confusing and might be error-prone. This is the price you pay for "implicitness" and concise code.
However note that no modified and val modifier before constructor argument are different. Without modified immutable field is created, while val additionally adds getter.

Scala creates a field from a constructor parameter when such an parameter is referenced by a method in the class. I'm having trouble finding fault with the way that this works.
For the simple case everything works as expected:
scala> class Bar(val url: String)
defined class Bar
scala> class Foo(url: String) extends Bar(url) {
| def say() { println(url) }
| }
defined class Foo
scala> new Foo("urlvalue").say
urlvalue
If we introduce some confusion over the case of the constructor parameter this example still works as expected:
scala> class Bar(val Url: String)
defined class Bar
scala> class Foo(url: String) extends Bar(url) {
| def say() { println(url) }
| }
defined class Foo
scala> new Foo("urlvalue").say
urlvalue
Interestingly you might think that this has worked because it has introduced a lower-case url field in Foo in addition to the upper case Url in Bar, but that doesn't seem to be the case - the compiler seems to be smart enough to know that it can go to Url to get the value of url in say, as no lower case field is generated.
scala> :javap -private Bar
Compiled from "<console>"
public class Bar extends java.lang.Object implements scala.ScalaObject{
private final java.lang.String Url;
public java.lang.String Url();
public Bar(java.lang.String);
}
scala> :javap -private Foo
Compiled from "<console>"
public class Foo extends Bar implements scala.ScalaObject{
public void say();
public Foo(java.lang.String);
}
The only time I can see that this gets confusing is if you mis-spell a var field. In this case you do actually introduce a new field, and the two can get out of step.
scala> class Bar(var Url: String)
defined class Bar
scala> class Foo(url: String) extends Bar(url) {
| def say() { println(url) }
| }
defined class Foo
scala> val f = new Foo("urlvalue")
f: Foo = Foo#64fb7efa
scala> f.say
urlvalue
scala> f.Url = "newvalue"
f.Url: String = newvalue
scala> f.say
urlvalue
scala> :javap -private Foo
Compiled from "<console>"
public class Foo extends Bar implements scala.ScalaObject{
private final java.lang.String url;
public void say();
public Foo(java.lang.String);
}

Related

Scala interop with Java questionmark generics

EDIT I have made the example self-contained.
Suppose in java I have
src/main/java/FooFactory.java
interface FooFactory {
Foo<?> create();
<T> void enhance(Foo<T> foo, FooEnhancer<? super T> enhancer);
}
and src/main/java/Foo.java
interface Foo<T> {}
and src/main/java/FooEnhancer.java
interface FooEnhancer<T> {}
(and you can't change these interfaces because they belong to someone else.)
Then in scala you have
object DummyFooEnhancer extends FooEnhancer[Any]
trait FooHdlr {
def fooFactory: FooFactory
val foo = fooFactory.create
fooFactory.enhance(foo, DummyFooEnhancer)
}
This doesn't compile because FooEnhancer and Foo are invariant but foo is a Foo<?> whereas DummyFooEnhancer is a Foo[Any]. Changing Any to AnyRef doesn't work for the same reason.
Then I reasoned that, well, ? is a specific type that is certainly not known at compile time and that may not necessarily be AnyRef/Object, so what if I do this?
case class DummyFooEnhancer[T] extends FooEnhancer[T]
trait FooHdlr {
def fooFactory: FooFactory
val foo = fooFactory.create
fooFactory.enhance(foo, new DummyFooEnhancer)
}
But this causes scalac to stackoverflow! It's looping with
at scala.reflect.internal.Types$TypeVar.isGround(Types.scala:3082)
calling itself.
Interestingly if I replace ? super T with T it works fine, but in real life I can't do this because it's in code I don't control

Why can't I have a private var / val with the same name as a getter / setter? or how to desugar implicit getters / setters

I'm sure it's a duplicate, but I couldn't find it.
Consider this class
class Test1(var param: Int)
Scala generates a getter and a setter, and makes the param private.
It can be shown using javap for example:
public class Test1 {
private int param;
public int param();
public void param_$eq(int);
public Test(int);
}
I was trying to write the desugared version of it with explicit getters / setters but couldn't do so 100% the same way as the private var had a naming collision with the getter.
class Test2(private[this] var param:Int) {
def param:Int = this.param
def param_= (param:Int) {this.param = param}
}
This is the error:
ambiguous reference to overloaded definition,
both method param in class Test2 of type => Int
and variable param in class Test2 of type Int
match expected type Int
def param:Int = param
^
This of course works (renaming private member to _param)
class Test3(private[this] var _param:Int) {
def param:Int = this._param
def param_= (param:Int) {this._param = param}
}
But generates this slightly different bytecode of course (since we had to rename param to _param):
public class Test3 {
private int _param;
public int param();
public void param_$eq(int);
public Test3(int);
}
Is there any way to reach the same bytecode as the example in Test1 while using explicit getter / setters as in Test2?
In Scala methods and fields share the same name space. This is a little confusing for converts coming from Java or other languages which have separate name space for each.
But it follows the Uniform Access Principle i.e. the user of a class can't tell if he is actually calling a def or a val (or a var) when used without parenthesis. This also means you can switch implementations between val an def without clients being affected. (I'm not sure if they'd have to get recompiled, but the source code can stay as it is.
Actually, you can!
Here is an answer that does exactly that to good effect.
There you will find a private var and an accessor with the same name.
private[this] var state
private final def state()
Note the parens on the definition of the accessor, which are not needed at the use site.
Under the covers, the local field has that LOCAL_SUFFIX_STRING, which is an evil space. See the Test3 members, below, which strips away the covers to bare all.
On the getter side, you can add parens. That breaks the saccharin setter, apparently. Can you live without sugary assignment syntax?
When you reference x.param, the compiler will supply parens, see #alexiv 's comment elsewhere on uniform access or the SO answer mentioned above.
import reflect.runtime.currentMirror
import reflect.runtime.universe._
class Test2(private[this] var param: Int) {
def param(): Int = param
def param_=(param: Int) { this.param = param }
}
val x = new Test2(1)
Console println x.param
x.param_=(2)
Console println x.param
class Test3(var param: Int)
val ps = typeOf[Test3].members filter (m => m.name.toString.startsWith("param")) map (m => s"'${m.name.toString}'")
Console println ps
Console println (typeOf[Test3].members filter (_.name.toString.endsWith(nme.LOCAL_SUFFIX_STRING)))
Note that the other answers suggest that Scala only distinguishes terms and types, neglecting to mention that symbols (obviously) can be overloaded.
You may have read that overloading is evil! That may be so, but whatever you do in the privacy of your own private members is entirely your own affair.
(Updated to suggest that even if the question is somewhat duplicated somewhere, this is still a fun and informative answer.)
I think variable and method are treated the same, the variable param is like a method without parameters, and the method param is the same, so just like the error indicates: ambiguous reference to overloaded definition.

Is there any advantage to definining a val over a def in a trait?

In Scala, a val can override a def, but a def cannot override a val.
So, is there an advantage to declaring a trait e.g. like this:
trait Resource {
val id: String
}
rather than this?
trait Resource {
def id: String
}
The follow-up question is: how does the compiler treat calling vals and defs differently in practice and what kind of optimizations does it actually do with vals? The compiler insists on the fact that vals are stable — what does in mean in practice for the compiler? Suppose the subclass is actually implementing id with a val. Is there a penalty for having it specified as a def in the trait?
If my code itself does not require stability of the id member, can it be considered good practice to always use defs in these cases and to switch to vals only when a performance bottleneck has been identified here — however unlikely this may be?
Short answer:
As far as I can tell, the values are always accessed through the accessor method. Using def defines a simple method, which returns the value. Using val defines a private [*] final field, with an accessor method. So in terms of access, there is very little difference between the two. The difference is conceptual, def gets reevaluated each time, and val is only evaluated once. This can obviously have an impact on performance.
[*] Java private
Long answer:
Let's take the following example:
trait ResourceDef {
def id: String = "5"
}
trait ResourceVal {
val id: String = "5"
}
The ResourceDef & ResourceVal produce the same code, ignoring initializers:
public interface ResourceVal extends ScalaObject {
volatile void foo$ResourceVal$_setter_$id_$eq(String s);
String id();
}
public interface ResourceDef extends ScalaObject {
String id();
}
For the subsidiary classes produced (which contain the implementation of the methods), the ResourceDef produces is as you would expect, noting that the method is static:
public abstract class ResourceDef$class {
public static String id(ResourceDef $this) {
return "5";
}
public static void $init$(ResourceDef resourcedef) {}
}
and for the val, we simply call the initialiser in the containing class
public abstract class ResourceVal$class {
public static void $init$(ResourceVal $this) {
$this.foo$ResourceVal$_setter_$id_$eq("5");
}
}
When we start extending:
class ResourceDefClass extends ResourceDef {
override def id: String = "6"
}
class ResourceValClass extends ResourceVal {
override val id: String = "6"
def foobar() = id
}
class ResourceNoneClass extends ResourceDef
Where we override, we get a method in the class which just does what you expect. The def is simple method:
public class ResourceDefClass implements ResourceDef, ScalaObject {
public String id() {
return "6";
}
}
and the val defines a private field and accessor method:
public class ResourceValClass implements ResourceVal, ScalaObject {
public String id() {
return id;
}
private final String id = "6";
public String foobar() {
return id();
}
}
Note that even foobar() doesn't use the field id, but uses the accessor method.
And finally, if we don't override, then we get a method which calls the static method in the trait auxiliary class:
public class ResourceNoneClass implements ResourceDef, ScalaObject {
public volatile String id() {
return ResourceDef$class.id(this);
}
}
I've cut out the constructors in these examples.
So, the accessor method is always used. I assume this is to avoid complications when extending multiple traits which could implement the same methods. It gets complicated really quickly.
Even longer answer:
Josh Suereth did a very interesting talk on Binary Resilience at Scala Days 2012, which covers the background to this question. The abstract for this is:
This talk focuses on binary compatibility on the JVM and what it means
to be binary compatible. An outline of the machinations of binary
incompatibility in Scala are described in depth, followed by a set of rules and guidelines that will help developers ensure their own
library releases are both binary compatible and binary resilient.
In particular, this talk looks at:
Traits and binary compatibility
Java Serialization and anonymous classes
The hidden creations of lazy vals
Developing code that is binary resilient
The difference is mainly that you can implement/override a def with a val but not the other way around. Moreover val are evaluated only once and def are evaluated every time they are used, using def in the abstract definition will give the code who mixes the trait more freedom about how to handle and/or optimize the implementation. So my point is use defs whenever there isn't a clear good reason to force a val.
A val expression is evaluated once on variable declaration, it is strict and immutable.
A def is re-evaluated each time you call it
def is evaluated by name and val by value. This means more or less that val must always return an actual value, while def is more like a promess that you can get a value when evaluating it. For example, if you have a function
def trace(s: => String ) { if (level == "trace") println s } // note the => in parameter definition
that logs an event only if the log level is set to trace and you want to log an objects toString. If you have overriden toString with a value, then you need to pass that value to the trace function. If toString however is a def, it will only be evaluated once it's sure that the log level is trace, which could save you some overhead.
def gives you more flexibility, while val is potentially faster
Compilerwise, traits are compiled to java interfaces so when defining a member on a trait, it makes no difference if its a var or def. The difference in performance would depend on how you choose to implement it.

Automatically make getters for class parameters (to avoid case classes)?

In Scala, if we have
class Foo(bar:String)
We can create a new object but cannot access bar
val foo = new Foo("Hello")
foo.bar // gives error
However, if we declare Foo to be a case class this works:
case class Foo(bar:String)
val foo = Foo("hello")
foo.bar // works
I am forced to make many of my classes as case classes because of this. Otherwise, I have to write boilerplate code for accessing bar:
class Foo(bar:String) {
val getbar = bar
}
So my questions are:
Is there any way to "fix" this without using case classes or boilerplate code?
Is using case classes in this context a good idea? (or what are the disadvantages of case classes?)
I guess the second one deserves a separate question.
Just use val keyword in constructor to make it publicly accessible:
class Foo(val bar:String) {
}
As for your question: if this is the only feature you need, don't use case classes, just write with val.
However, would be great to know why case classes are not good.
In case classes all arguments by default are public, whereas in plain class they're all private. But you may tune this behaviour:
scala> class Foo(val bar:String, baz: String)
defined class Foo
scala> new Foo("public","private")
res0: Foo = Foo#31d5e2
scala> res0.bar
res1: String = public
scala> res0.baz
<console>:10: error: value baz is not a member of Foo
res0.baz
And even like that:
class Foo(private[mypackage] val bar:String) {
// will be visible only to things in `mypackage`
}
For case classes (thanks to #JamesIry):
case class Bar(`public`: String, private val `private`: String)
You can use the BeanProperty annotation to automatically generate java-like getters
import scala.reflect.BeanProperty
case class Foo(#BeanProperty bar:String)
Now Foo has a getBar method that returns the bar value.
Note though that this is only useful if you have a good reason to use java-like getters (typical reasons being that you need your class to be a proper java bean, so as to work with java libraries that expect java beans and use reflection to access the bean's properties).
Otherwise, just access the bar value directly, this is "the scala way".
See http://www.scala-lang.org/api/current/index.html#scala.reflect.BeanProperty

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