I would like to automatically weave the definition of a new function say introduced by an extending trait Ext into an abstract class A:
class Base {
abstract class A
class B extends A
case class C extends A
}
trait Ext extends Base {
trait A extends super.A {
def say = "hello"
}
}
object Test extends Base with Ext {
val b = new B
b.say
}
However, I obtain the following error:
<console>:12: error: value say is not a member of Test.B
b.say
Any way of doing it?
It seems you are trying to use virtual classes, which is a feature not available in Scala.
Once A and B are defined they can't be redefined (like method overriding).
abstract class A
class B extends A
On the other hand, given your example, your objective could be achieved by a simple mixin. Here it is with few rewrites:
class Base {
abstract class A
class B extends A
case class C extends A
}
trait Ext extends Base {
trait CanSay extends A {
def say = "hello"
}
}
object Test extends Base with Ext {
val b = new B with CanSay
def apply = b.say
}
Test.apply
No sure it will really help, but at least will help you understand what is going on.
Okay, as I said in a comment, it's not entirely clear what you're trying to do here, so I can't really try to suggest ways to do it. However, the approach you're using at the moment will not work.
Consider the class Hierarchy in this situation. At the base, we have A, which is then subclassed with B (in Base) and with Ext.A. These are not related save by their shared supertype, so you'll never find a say method on an instance of B.
The confusion possibly arises through the use of the word abstract. An abstract modifier on a class (even an inner class) does not make it an abstract member of the parent class, but denotes that it itself may have abstract members. There are ways of giving a class an abstract class member - through type parameters or type members. Unfortunately, you cannot derive from these AFAIK.
Related
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.
Scala throws "reassignment to val" error for the following code.
abstract case class Gun(var bulletCount:Int)
class Pistol(bulletCount:Int) extends Gun(bulletCount){
def fire() { bulletCount=bulletCount-1 }
}
Anything I missed here?
For starters, you should consider case class as final, and not extend them.
Second, do not use var with case class, you should rather create a copy of a case class to get one of its field changed.
Third, if you want a common type, you can use a base trait.
All in one, here's what it could look like:
sealed trait Gun {
def bulletCount: Int
}
case class Pistol(bulletCount: Int) extends Gun {
def fire(): Pistol = copy(bulletCount=bulletCount)
}
You're referring to bulletCount field generated by Pistol primary constructor parameter. To set base class variable, you need to directly call field using super:
class Pistol(bulletCount: Int) extends Gun(bulletCount) {
def fire(): Unit = {
super.bulletCount = super.bulletCount - 1
}
}
Alternatively, you can label parameter-generated field with override var:
class Pistol(override var bulletCount: Int) extends Gun(bulletCount) {
def fire(): Unit = {
bulletCount = bulletCount - 1
}
}
On a side note, as Frederic A. suggested in his answer, you should avoid inheriting case classes. They are syntactic sugar, and code generation don't work over inheritance - you'll need to implement all the fancy stuff like apply or unapply methods in companion class all by yourself. Scala compiler team tried to support case class to case class inheritance, but discovered that it breaks structural equality and lots of other things.
I have an abstract class Model from which I create case classes:
abstract class Model
case class User(.) extends Model
an abstract class Table taking such a Model as type parameter, used in one of its default concrete methods:
abstract class Table[M <: Model] {
def parser = SomeExternalBuilder[M]
}
The meaning is rather simple: "Give every instance of Table a default parser based on its own class".
The problem is that SomeExternalBuilder will only accept a case class as argument ("case class expected: M"), so it does not compile.
Can I make Table take only case classes as type parameter?
I have seen a few answers providing a missing copy method (ref1, ref2), so I tried this:
trait Model[T] {
def copy: T
}
abstract class Table[M <: Model[M]]
but now case class User extends Model[User] and must overwrite copy too, every function creating a Model takes a type parameter, and honestly the code quickly starts being atrocious, all that for that single line in Table.
Is there no better way than copying that def parser line in every child's body?
Edit: N.B. The real function is def parser: anorm.Macro.namedParser[M] from the "anorm" library for Play.
Edit: Source of the type check by this macro: https://github.com/playframework/anorm/blob/0a1b19055ba3e3749044ad8a54a6b2326235f7c8/core/src/main/scala/anorm/Macro.scala#L117
The problem is that SomeExternalBuilder will only accept a case class as argument ("case class expected: M"), so it does not compile.
I don't think you can ever get such a message from Scala compiler itself, which means that SomeExternalBuilder.apply is a macro. It requires a specific case class in order to know its fields, so that it doesn't matter if you could limit M to be a case class (which you can't): it still wouldn't accept a type parameter.
What you can do is create a macro annotation, so that when you write e.g.
#HasModel
class SomeTable extends Table[SomeModel] {
...
}
the val parser = namedParser[SomeModel] is generated automatically.
Alternately, write #HasModel[SomeModel] class SomeTable { ... } and generate extends Table[SomeModel] as well.
It wouldn't be hard (as macros go), but you still need to annotate each class extending Table.
Not fool proof solution but worth a try
case classes extend Product and Serialisable. Constraint Product with Serialisable will help you get some type safety. M can be any class which extends Product with Serialisable. But Product is extended by case class mostly
abstract class Table[M <: (Product with Serializable)] {
def parser = SomeExternalBuilder[M]
}
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()
Quick note: Examples from the tutorial Scala for Java Refugees Part 5: Traits and Types.
Suppose I have the traits Student, Worker, Underpaid, and Young.
How could I declare a class (not instance), CollegeStudent, with all these traits?
Note: I am aware of the simplests cases, such as CollegeStudent with one or two Traits:
class CollegeStudent extends Student with Worker
It is easy, when declaring a class you just use the "with" keyword as often as you want
class CollegeStudent extends Student with Worker with Underpaid with Young
the order of the traits can be important if a trait is changing the behavior of the class, it all depends on traits you are using.
Also if you don't want to have a class which always uses the same traits you can use them later:
class CollegeStudent extends Student
new CollegeStudent with Worker with Underpaid with NotSoYoungAnymore
I think that it is very important to explain not only the syntax, but also which role does the ordering of the traits play. I found the explanation in Jason Swartz's Learning Scala (page 177) quite enlightning.
A Scala class can extend multiple traits at once, but JVM classes can extend only one parent class. The Scala compiler solves this by creating "copies of each trait to form a tall, single-column hierarchy of the
class and traits", a process known as linearization.
In this context, extending multiple traits with identical field names would fail to compile, exactly the same "as if you were extending a class and providing your own version of a method but failed to add an override keyword".
And since it determines the shape of the inheritance tree, the linearization order is indeed one very important question to regard. As an example, class D extends A with B with C (where A is a class and B
and C are traits) would become class D extends C extends B extends A. The following few lines, also from the book, illustrate that perfectly:
trait Base { override def toString = "Base" }
class A extends Base { override def toString = "A->" + super.toString }
trait B extends Base { override def toString = "B->" + super.toString }
trait C extends Base { override def toString = "C->" + super.toString }
class D extends A with B with C { override def toString = "D->" + super.toString }
A call to new D() would have the REPL print the following:
D->C->B->A->Base
Which perfectly reflects the structure of the linearized inheritance graph.