Case class copy() method abstraction - scala

I would like to know if it is possible to abstract the copy method of case classes. Basically I have something like sealed trait Op and then something like case class Push(value: Int) extends Op and case class Pop() extends Op.
The first problem: A case class without arguments/members does not define a copy method. You can try this in the REPL.
scala> case class Foo()
defined class Foo
scala> Foo().copy()
<console>:8: error: value copy is not a member of Foo
Foo().copy()
^
scala> case class Foo(x: Int)
defined class Foo
scala> Foo(0).copy()
res1: Foo = Foo(0)
Is there a reason why the compiler makes this exception? I think it is rather unituitive and I would expect every case class to define a copy method.
The second problem: I have a method def ops: List[Op] and I would like to copy all ops like ops map { _.copy() }. How would I define the copy method in the Op trait? I get a "too many arguments" error if I say def copy(): Op. However, since all copy() methods have only optional arguments: why is this incorrect? And, how do I do that correct? By making another method named def clone(): Op and write everywhere def clone() = copy() for all the case classes? I hope not.

You seem to be confusing copy with clone. The goal of copy is to make an almost identical copy, but with something changed. What that something might be depends on the parameters of the case class, so it's not possible to make it a common method.
In the case of case class X(), it doesn't make much sense to have a copy method, as there's nothing there to be changed.
On the other hand, clone is a Java method whose goal is to produce perfect copies of an object, which seems to be what you want.

What would be the benefit of a compiler generated copy method for case classes without any arguments? This would just return a new Foo, and not copy anything.
To quote Lukas Rytz (I believe he implemented it):
The copy methods are only generated if there is no member named"copy" in the class, directly defined or inherited.

Upvoted Ben's answer. But what if you wanted to something like this:
sealed trait Op
case class Push(value: Int, context:String) extends Op
case class Pop(context:String) extends Op
val stackOps = List(Push(3, "foo"), Pop("foo"))
def copyToContext(newContext:String, ops:List[Op]): List[Op] = {
// ... ?
}
val changedOps = copyToContext("bar", stackOps)
// would return: List(Push(3, "bar"), Pop("bar"))

As Mirko correctly pointed out, you cannot really abstract over copy method. I support Daniel's view, that cloning may be what you want, although I would wrap it with some helper code to reduce boilerplate.
You can define a mixin trait with copy functionality and just mix it into your case classes then:
trait ClonableAs[T] extends Cloneable { this: T =>
def makeClone() = super.clone().asInstanceOf[T]
}
case class Foo(i: Int) extends ClonableAs[Foo]
List(Foo(1), Foo(2), Foo(3)).map(_.makeClone())
That way instead of adding an identical method to each of your case classes, you make them extend the helper trait, which makes them cleaner and saves you some keystrokes.
On the other hand, the cloning would make no sense for immutable objects, so I infer your classes have mutable state. I would advise you to reconsider if you really cannot make them immutable, and use that type of cloning only at last resort. Immutability will protect yourself from a class of errors.

Why do you need to create identical copies of your case class instances? Case classes are, by default, immutable so can be safely shared.
In any case, I don't think you can do what you're asking with default parameters:
scala> trait Op { def copy():Op }
defined trait Op
scala> case class Op1(v:Int) extends Op
<console>:6: error: class Op1 needs to be abstract, since method copy in trait Op of type ()Op is not defined
case class Op1(v:Int) extends Op
The compiler doesn't create methods with all combinations of the optional parameters in the defining class. The default values are inserted in the place where the method is called.

Related

Define common copy for a class hierarchy with many case classes

I would like to define a class hierarchy with about 100 case classes deriving from common base. The types are describing nodes in the AST hierarchy, like this one. I would like to do something along the lines of:
trait Base {
def doCopy: Base
}
trait CloneSelf[T <: CloneSelf[T]] extends Base {
self: T =>
def copy(): T
override def doCopy: T = copy()
}
case class CaseA(a: String) extends Base with CloneSelf[CaseA]
case class CaseB(b: Int) extends Base with CloneSelf[CaseB]
This gives an error, because the existence of my copy prevents the case classes from defining the automatic copy. Is there some way how to implement the "clone" doCopy so that is uses the automatic copy of those case classes?
I would like to define a class hierarchy with about 100 case classes deriving from common base.
Please do not do that, you should absolutely find a pattern to avoid it! If you want to do this anyway... Try ducktyping:
trait CloneSelf[T <: {def copy(): T}] {
self: T =>
override def doCopy: T = copy()
}
I cannot test now so this probably won't compile, but you can figure it out by yourself with the general idea!
Edit:
Why having 100 subclasses is evil: imagine you perform one change in the base class, for instance change its name from Base to BaseCloning -> you'll have to change it in EVERY child class (100 changes).
How you will avoid that depends on what you want to do with your classes, check creationnal and structural patterns: factory, builder, prototype, flyweight, composite... Always think about "how much work will I have if I change something in the base class? Will it affect all children?"
I have found out defining the doCopy in each case class is actually less work than defining each class to inherit from CloneSelf. The code looks like this:
trait Base {
def doCopy: Base
}
case class CaseA(a: String) extends Base {
def doCopy = copy()
}
case class CaseB(b: Int) extends Base {
def doCopy = copy()
}
I was surprised to learn that without explicit type on the overridden method the type is inferred by the compiler, therefore the static type of CaseA("a").doCopy is the same as of CaseA("a").copy(), i.e. CaseA, not Base. Adding explicit type for each case class would be probably more obvious, but this would require more work compared to just copy-pasting the same line into each of them. Not that it matters much - when I do copying via the case class type, I may use the copy() as well. It is only when I have the Base I need the doCopy, therefore declaring it like def doCopy: Base = copy() would do little harm.

In Scala, How to perform compile-time type check on companion object?

An easy thing to do in many languages but not in Scala is:
Define archetype 'Super', such that all implementations of 'Super' has to define a constructor 'create()'.
I found this constraint very important and is able to identify a lot of problems before runtime. However this feature is only partially enforced in Java (by defining an 'abstract' static method that always throws an error) and completely missing in Scala (companion object is completely detached from class and cannot be enforced in archetype).
is there a macro or tool that allows me to do this?
UPDATE Sorry my question was missing context and examples. Here is a formal use case in scala:
In project A, we define an interface that can be extended by all subprojects:
trait AbstractFoo {}
This interface should always have a default 0-parameter builder/constructor, so project A can initialize it on-demand, however, the implementation of each constructor is unknown to project A:
object AbstractFoo {
def default[T <: AbstractFoo: ClassTag](): T
}
So the problem becomes: How to rigorously define AbstractFoo, such that for all subprojects of A, any implementation(s) of AbstractFoo:
case class Foo(...) extends AbstractFoo
must satisfy:
'Foo' must have a 0-parameter builder/constructor defined (presumably in its companion object)
calling AbstractFoo.defaultFoo can invoke this 0-parameter builder/constructor
It should be noted that in an alternative conditions, a solution exists which is to define every companion object as an implicit type class:
trait FooBuilder[T <: AbstractFoo] {
def default(): T
}
object AbstractFoo {
implicit object Foo extends FooBuilder[Foo] {
def default() = {...}
}
def default[T <: AbstractFoo: FooBuilder](): T = {
implicitly[FooBuilder[T]].default
}
}
Such that if the implicit object is undefined the compiler will give an implicit not found error (my code snippet may have some syntax error, the idea is from http://www.cakesolutions.net/teamblogs/demystifying-implicits-and-typeclasses-in-scala)
Unfortunately it's not always convenient, because this subproject of A is usually unknown to project A. Yet the default implicit builder cannot be redefined, this makes every invocation of default() more covoluted.
I believe scala is a very extendable language, so there should be at least 1 way to enforce it whether if using macro, annotation or other metaprogramming techniques. Is my question clear enough now?
UPDATE2: I believe I found the solution after carefully study Scaladoc, there is a comment hidden in a corner:
if there are several eligible arguments which match the implicit parameter’s type, a most specific one will be chosen using the rules of static overloading resolution (see Scala Specification §6.26.4):
...
Implicit scope of type arguments (2.8.0)
...
So all I need is to write an implicit function in FooBuilder:
trait FooBuilder[T <: AbstractFoo] {
def default(): T
implicit def self = this
}
object Foo extends FooBuilder[Foo]
So everytime someone call:
default[Foo]
scala will refer to the scope of class Foo, which include object Foo, which contains the implicit value Foo, and eventually find the 0-parameter constructor.
I think this definition is better than defining it under object FooBuilder, since you can only define FooBuilder once, thus its not quite extendable. Would you agree with me? If so, could you please revise your answer so I can award you point?
I don't understand why an abstract class or even a Trait won't allow this to be done?
abstract class DefineCreate{
def create(): Unit
}
case class Foo(one: Int)
object Foo extends DefineCreate{
def create(): Unit = { Console.out.println("side-effect") }
}
Thus I force a user to make a create method on the object in question because all implementations of DefineCreate must do so in order to compile.
Update Following Comments
Well, without having to resort to macros and the like, you could achieve the same sort of thing with type classes:
trait Constructor[A]{
def create(): A
}
object Construct{
def create[A](implicit cr: Constructor[A]): A = cr.create()
}
Which doesn't explicitly force the companion object to sprout methods but it does force a user to make the type class if they want to use the Constructor.create[Foo] pattern.

In Scala is there a way to reference the Companion Object from within an instance of a Case Class?

In my specific case I have a (growing) library of case classes with a base trait (TKModel)
Then I have an abstract class (TKModelFactory[T <: TKModel]) which is extended by all companion objects.
So my companion objects all inherently know the type ('T') of "answers" they need to provide as well as the type of objects they "normally" accept for commonly implemented methods. (If I get lazy and cut and paste chunks of code to search and destroy this save my bacon a lot!) I do see warnings on the Internet at large however that any form of CompanionObject.method(caseClassInstance: CaseClass) is rife with "code smell" however. Not sure if they actually apply to Scala or not?
There does not however seem to be any way to declare anything in the abstract case class (TKModel) that would refer to (at runtime) the proper companion object for a particular instance of a case class. This results in my having to write (and edit) a few method calls that I want standard in each and every case class.
case class Track(id: Long, name: String, statusID: Long) extends TKModel
object Track extends TKModelFactory[Track]
How would I write something in TKModel such that new Track(1, "x", 1).someMethod() could actually call Track.objectMethod()
Yes I can write val CO = MyCompanionObject along with something like implicit val CO: ??? in the TKModel abstract class and make all the calls hang off of that value. Trying to find any incantation that makes the compiler happy for that however seems to be mission impossible. And since I can't declare that I can't reference it in any placeholder methods in the abstract class either.
Is there a more elegant way to simply get a reference to a case classes companion object?
My specific question, as the above has been asked before (but not yet answered it seems), is there a way to handle the inheritance of both the companion object and the case classes and find the reference such that I can code common method calls in the abstract class?
Or is there a completely different and better model?
If you change TKModel a bit, you can do
abstract class TKModel[T <: TKModel] {
...
def companion: TKModelFactory[T]
def someMethod() = companion.objectMethod()
}
case class Track(id: Long, name: String, statusID: Long) extends TKModel[Track] {
def companion = Track
}
object Track extends TKModelFactory[Track] {
def objectMethod() = ...
}
This way you do need to implement companion in each class. You can avoid this by implementing companion using reflection, something like (untested)
lazy val companion: TKModelFactory[T] = {
Class.forName(getClass.getName + "$").getField("MODULE$").
get(null).asInstanceOf[TKModelFactory[T]]
}
val is to avoid repeated reflection calls.
A companion object does not have access to the instance, but there is no reason the case class can't have a method that calls the companion object.
case class Data(value: Int) {
def add(data: Data) = Data.add(this,data)
}
object Data {
def add(d1: Data, d2: Data): Data = Data(d1.value + d2.value)
}
It's difficult. However you can create an implicit method in companion object. whenever you want to invoke your logic from instance, just trigger implicit rules and the implicit method will instantiate another class which will invoke whatever logic you desired.
I believe it's also possible to do this in generic ways.
You can implement this syntax as an extension method by defining an implicit class in the top-level abstract class that the companion objects extend:
abstract class TKModelFactory[T <: TKModel] {
def objectMethod(t: T)
implicit class Syntax(t: T) {
def someMethod() = objectMethod(t)
}
}
A call to new Track(1, "x", 1).someMethod() will then be equivalent to Track.objectMethod(new Track(1, "x", 1)).

Implementing '.clone' in Scala

I'm trying to figure out how to .clone my own objects, in Scala.
This is for a simulation so mutable state is a must, and from that arises the whole need for cloning. I'll clone a whole state structure before moving the simulation time ahead.
This is my current try:
abstract trait Cloneable[A] {
// Seems we cannot declare the prototype of a copy constructor
//protected def this(o: A) // to be defined by the class itself
def myClone= new A(this)
}
class S(var x: String) extends Cloneable[S] {
def this(o:S)= this(o.x) // for 'Cloneable'
def toString= x
}
object TestX {
val s1= new S("say, aaa")
println( s1.myClone )
}
a. Why does the above not compile. Gives:
error: class type required but A found
def myClone= new A(this)
^
b. Is there a way to declare the copy constructor (def this(o:A)) in the trait, so that classes using the trait would be shown to need to provide one.
c. Is there any benefit from saying abstract trait?
Finally, is there a way better, standard solution for all this?
I've looked into Java cloning. Does not seem to be for this. Also Scala copy is not - it's only for case classes and they shouldn't have mutable state.
Thanks for help and any opinions.
Traits can't define constructors (and I don't think abstract has any effect on a trait).
Is there any reason it needs to use a copy constructor rather than just implementing a clone method? It might be possible to get out of having to declare the [A] type on the class, but I've at least declared a self type so the compiler will make sure that the type matches the class.
trait DeepCloneable[A] { self: A =>
def deepClone: A
}
class Egg(size: Int) extends DeepCloneable[Egg] {
def deepClone = new Egg(size)
}
object Main extends App {
val e = new Egg(3)
println(e)
println(e.deepClone)
}
http://ideone.com/CS9HTW
It would suggest a typeclass based approach. With this it is possible to also let existing classes be cloneable:
class Foo(var x: Int)
trait Copyable[A] {
def copy(a: A): A
}
implicit object FooCloneable extends Copyable[Foo] {
def copy(foo: Foo) = new Foo(foo.x)
}
implicit def any2Copyable[A: Copyable](a: A) = new {
def copy = implicitly[Copyable[A]].copy(a)
}
scala> val x = new Foo(2)
x: Foo = Foo#8d86328
scala> val y = x.copy
y: Foo = Foo#245e7588
scala> x eq y
res2: Boolean = false
a. When you define a type parameter like the A it gets erased after the compilation phase.
This means that the compiler uses type parameters to check that you use the correct types, but the resulting bytecode retains no information of A.
This also implies that you cannot use A as a real class in code but only as a "type reference", because at runtime this information is lost.
b & c. traits cannot define constructor parameters or auxiliary constructors by definition, they're also abstract by definition.
What you can do is define a trait body that gets called upon instantiation of the concrete implementation
One alternative solution is to define a Cloneable typeclass. For more on this you can find lots of blogs on the subject, but I have no suggestion for a specific one.
scalaz has a huge part built using this pattern, maybe you can find inspiration there: you can look at Order, Equal or Show to get the gist of it.

Pattern matching with generics

Given the following class pattern match:
clazz match {
case MyClass => someMethod[MyClass]
}
Is it possible to refer to MyClass in a generic way based on what the pattern match came up with? For example, if I have multiple subclasses of MyClass, can I write a simple pattern match to pass the matched type to someMethod:
clazz match {
case m <: MyClass => someMethod[m]
}
Unfortunately types are not really first class citizens in Scala. This means for example that you cannot do pattern matching on types. A lot of information is lost due to stupid type erasure inherited from the Java platform.
I don't know if there are any improvement requests for this, but this is one of the worst problems in my option, so someone should really come up with such a request.
The truth is you will need to pass around evidence parameters, at best in the form of implicit parameters.
The best I can think of goes in the line of
class PayLoad
trait LowPriMaybeCarry {
implicit def no[C] = new NoCarry[C]
}
object MaybeCarry extends LowPriMaybeCarry {
implicit def canCarry[C <: PayLoad](c: C) = new Carry[C]
}
sealed trait MaybeCarry[C]
final class NoCarry[C] extends MaybeCarry[C]
final class Carry[C <: PayLoad] extends MaybeCarry[C] {
type C <: PayLoad
}
class SomeClass[C <: PayLoad]
def test[C]( implicit mc: MaybeCarry[C]) : Option[SomeClass[_]] = mc match {
case c: Carry[_] => Some(new SomeClass[ c.C ])
case _ => None
}
but still I can't get the implicits to work:
test[String]
test[PayLoad] // ouch, not doin it
test[PayLoad](new Carry[PayLoad]) // sucks
So if you want to save yourself serous brain damage, I would forget about the project or look for another language. Maybe Haskell is better here? I'm still hoping that we can eventually match types, but my hopes are pretty low.
Maybe the guys from scalaz have come up with a solution, they pretty much exploited the type system of Scala to the limits.
Your code is not really clear, because at least in java clazz is a typical name for variables of type java.lang.Class and variations. I still believe that clazz is not an instance of Class but of your own class.
In Java and Scala, given an object o: AnyRef you can get access to its class at runtime via o.getClass: Class[_], and for instance create instances of that class through the Reflection API. However, type parameters are passed at compile-time, so you can't pass a type as-is at compile time. Either you use AnyRef all over the place as type (which will work, I assume) or you use the reflection API if you have more advanced needs.