Define common copy for a class hierarchy with many case classes - scala

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.

Related

Scala - restricting access to generic case classes

If I want a case class that cannot be manually constructed from outside a package, standard way would be something like this:
case class Foo private[p](a:A,b:B)
object Foo{
def apply(c:C) = {
require tit
require tat
Foo(c.a,c.b)
}
}
Any way to do that if the class looks like this:
case class Bar[T <: MySomething[T]] private[p](t:T)
or will I have to content myself with writing a def that takes care of the case class creation and must be explicitly called?
Edit
Seems I wasn't clear about what my problem was ...
How can I pass the required parameters to the object / apply function?

Where would "abstract override" in subtrait with no implementation be useful, if any?

Given the following example of two traits with one extending another with no implementation of def a in each:
scala> trait A { def a: String }
defined trait A
scala> trait B extends A { abstract override def a: String }
defined trait B
Is the construct useful at all? What are the use cases?
I think the answer is essentially the same as the one linked in your comment. In Scala, the abstract keyword for methods isn't required, since the compiler can figure out whether it's abstract or not based on whether or not it has an implementation. So it's usage here is superfluous.
The override keyword is also not required for methods that are implementing an abstract method (or I guess not doing anything at all, in this case). So really, B is equivalent to:
trait B extends A { def a: String }
Or really just (since B will be assumed to be abstract):
trait B extends A
Similarly to the linked answer, I can imagine once scenario where using override might be useful for readability. If I were making the return type of a in B more specific than A, I could use override as a hint that I'm modifying the behavior in some way:
trait A {
def a: Any
}
trait B extends A {
override def a: String
}
In this case, I'm hinting that a in B might be slightly different than the inherited signature from A. Of course, this is only useful if it's known to the reader and used in a consistent manner. But I could still do the same thing without the override.
Short answer: abstract override is not useful in this case. It's basically like giving a type annotation where none would be needed.
The value added use of abstract override is for decorating an implementation that will be mixed in later, sometimes known as the "stackable trait pattern". See Why is "abstract override" required not "override" alone in subtrait?.
Abstract override indicates that you wish to override an 'abstract' method. Others address why it's useless here, so I'll focus on an example. Abstract override is best used for mixins. A simple example would be a Pollable trait:
trait Pollable{def poll:Double}
Lets say we want to weight this pollable. This trait will be a mixin for our trait. Our weighted pollable will have a weight field, which it will multiply a poll by to get a result. For example:
class OnePollable extends Pollable{
def poll:Double=1
}
val myWeightedOne=new OnePollable with WeightedPollable;
Lets try and write this trait:
//Does not compile
trait WeightedPollable extends Pollable{
var weight=1
def poll:Double=super.poll*weight
}
If you look, you'll see clearly why this doesn't work. Our trait tries to call a super type method that isn't implemented! One solution is to add a default to the super trait, Pollable:
//Don't do this!
trait Pollable{def poll:Double=1}
This sorta works here, but is sorta dumb in a lot of real world applications. The better way is this:
trait WeightedPollable extends Pollable{
var weight=1
abstract override def poll:Double=super.poll*weight
}
It's our friend the abstract override modifier! This tells the compiler that we are overriding an abstract method, but we want to use super to refer to an object we are being mixed into. This also disallows the trait being used as an interface.

Sealing a class which has many subtypes

I'm porting one of my C++ programs into Scala. In that project, there are hundreds of user-defined classes in an organised hierarchy. If I seal one of the top-level abstract classes, according to Scala rules, I have to put the definition of all subtypes of that class in the same file as the sealed class/trait. For one of my class hierarchies it would mean putting the definition of about 30 classes in that file.
In my C++ program these 30 classes are located in their own header and implementation files making them easier to maintain and read. I fear that if I put the definition of those 30 classes/objects in one file in my Scala application, it will make them hard to maintain and read.
The reason for sealing the class is so that I can do exhaustive searches when pattern matching on those types. Any help to point in me in the right direction with regards to organising Scala class hierarchies would be appreciated.
It's a bit of a pain to do this in separate classes, but it might be less painful than having everything in one huge file. First, you need to make sure you compile all the files together. Then, in your file where you make sure everything is sealed, you do the following:
trait GenericA { def foo: Int }
sealed trait A extends GenericA
case class B() extends A with ImplB {}
case class C() extends A with ImplC {}
...
The trick is that everything in the superclass (and it can be an abstract class instead of a trait if you wish) goes into GenericA. But you never actually use GenericA in your code, you just use A. Now, you can write a bunch of separate files with each implementation, defined like so:
// File #1
trait ImplB extends GenericA { def foo = 7 }
// File #2
trait ImplC extends GenericA { def foo = 4 }
...
Now you have your implementations separated out (at least those parts which can be expressed in terms of GenericA only).
What if you need the case class parameters available also? No problem--just include those as part of the trait.
// Main file
case class D(i: Int) extends A with ImplD {}
// Separate file
trait ImplD {
def i: Int
def foo = i*3
}
It's a bit of extra work since you have to repeat the case class parameters in two spots, but in your case it may be worth it.
Assuming your classes are case-classes which have many methods (which could make your file grow), you can try to separate definition from implementation using type classes (but sometimes it could afffect compiler's performance), like:
Model.scala
sealed trait A
case class A1(a: Int) extends A
case class A2(a: Int) extends A
case class A3(a: Int, b: Int) extends A
...
case class A1(a: Int) extends A
ImplA1.scala
package org.impl
implicit class ImplA1(a: A1) {
def method1() = a.a + a.a
}
ImplA2.scala
package org.impl
implicit class ImplA2(a: A2) {
def method1() = a.a * 2
}
Usage:
import org.impl._
val a1 = new A1
a1.method1()

How to design immutable model classes when using inheritance

I'm having trouble finding an elegant way of designing a some simple classes to represent HTTP messages in Scala.
Say I have something like this:
abstract class HttpMessage(headers: List[String]) {
def addHeader(header: String) = ???
}
class HttpRequest(path: String, headers: List[String])
extends HttpMessage(headers)
new HttpRequest("/", List("foo")).addHeader("bar")
How can I make the addHeader method return a copy of itself with the new header added? (and keep the current value of path as well)
Thanks,
Rob.
It is annoying but the solution to implement your required pattern is not trivial.
The first point to notice is that if you want to preserve your subclass type, you need to add a type parameter. Without this, you are not able to specify an unknown return type in HttpMessage
abstract class HttpMessage(headers: List[String]) {
type X <: HttpMessage
def addHeader(header: String):X
}
Then you can implement the method in your concrete subclasses where you will have to specify the value of X:
class HttpRequest(path: String, headers: List[String])
extends HttpMessage(headers){
type X = HttpRequest
def addHeader(header: String):HttpRequest = new HttpRequest(path, headers :+header)
}
A better, more scalable solution is to use implicit for the purpose.
trait HeaderAdder[T<:HttpMessage]{
def addHeader(httpMessage:T, header:String):T
}
and now you can define your method on the HttpMessage class like the following:
abstract class HttpMessage(headers: List[String]) {
type X <: HttpMessage
def addHeader(header: String)(implicit headerAdder:HeaderAdder[X]):X = headerAdder.add(this,header) }
}
This latest approach is based on the typeclass concept and scales much better than inheritance. The idea is that you are not forced to have a valid HeaderAdder[T] for every T in your hierarchy, and if you try to call the method on a class for which no implicit is available in scope, you will get a compile time error.
This is great, because it prevents you to have to implement addHeader = sys.error("This is not supported")
for certain classes in the hierarchy when it becomes "dirty" or to refactor it to avoid it becomes "dirty".
The best way to manage implicit is to put them in a trait like the following:
trait HeaderAdders {
implicit val httpRequestHeaderAdder:HeaderAdder[HttpRequest] = new HeaderAdder[HttpRequest] { ... }
implicit val httpRequestHeaderAdder:HeaderAdder[HttpWhat] = new HeaderAdder[HttpWhat] { ... }
}
and then you provide also an object, in case user can't mix it (for example if you have frameworks that investigate through reflection properties of the object, you don't want extra properties to be added to your current instance) (http://www.artima.com/scalazine/articles/selfless_trait_pattern.html)
object HeaderAdders extends HeaderAdders
So for example you can write things such as
// mixing example
class MyTest extends HeaderAdders // who cares about having two extra value in the object
// import example
import HeaderAdders._
class MyDomainClass // implicits are in scope, but not mixed inside MyDomainClass, so reflection from Hiberante will still work correctly
By the way, this design problem is the same of Scala collections, with the only difference that your HttpMessage is TraversableLike. Have a look to this question Calling map on a parallel collection via a reference to an ancestor type

Case class copy() method abstraction

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.