Generic "nested members" trait - scala

I am building a class composed of multiple traits, one of which provides the capability to include a list of "members" of the class:
trait WithNestedMembers[T] {
val members = new scala.collection.mutable.ListBuffer[T]
}
class MainClass extends WithNestedMembers[MainClass] {
// ...
}
This seems to work fine.
Now, I would like to use the "nesting" capability in other traits:
trait NestingUser {
this: WithNestedMembers[NestingUser] =>
var nestedValue = 0
def sumNested = nestedValue + members.map(_.nestedValue).sum
}
The definition seems to work fine by itself. However, I cannot use this as part of MainClass. The following gives an error:
class MainClass extends WithNestedMembers[MainClass] with NestingUser {
// Illegal inheritance, self-type MainClass does not conform to WithNestedMembers[NestingUser]
}
I suspect that I need to use some sort of type bounds somewhere, to explain that WithNestedMembers[MainClass] is good enough for requirement WithNestedMembers[NestingUser]. But where?

Trying making T covariant with +T like so
trait WithNestedMembers[+T] {
val members = List.empty[T]
}

Related

Reassignment to val error when var member of base class in scala

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.

Why can't I call a method with parameter `this` in a class extending a generic trait?

I'm writing a trait to describe node-like objects in a Hierarchical structure (like a graph). My working code is below:
trait Hierarchical[T <: Hierarchical[_]] {
// The parents will be a list of some arbitrary Hierarchical
val parents: List[Hierarchical[_]] = Nil
var children: List[Hierarchical[T]] = List()
def addChild(child: Hierarchical[T]): Unit = {
children ++= List(child)
}
}
abstract class NonRootNode(override val parents: List[Hierarchical[_]])
extends Hierarchical[NonRootNode] {
}
abstract class RootNode extends Hierarchical[NonRootNode] {
final override val parents = Nil
}
I have some tests which reflect this behavior:
import org.scalatest.FlatSpec
class DummyRoot extends RootNode
class DummyNonRoot(override val parents: List[DummyRoot])
extends NonRootNode(parents)
class TestUtil extends FlatSpec {
"A RootNode" should "not have parents" in {
val dummyRoot = new DummyRoot
assert(dummyRoot.parents.isEmpty)
}
"A NonRootNode" should "have parents when constructed with them" in {
val dummyRoot = new DummyRoot
assert(dummyRoot.parents.isEmpty)
val dummyNonRoot = new DummyNonRoot(List(dummyRoot))
dummyRoot.addChild(dummyNonRoot)
assert(dummyNonRoot.parents.contains(dummyRoot))
assert(dummyRoot.children.contains(dummyNonRoot))
}
}
However, I find the API to be a bit unwieldy in the second test. I shouldn't need to add a child to the root node explicitly since I've already specified the child's parents. I'd like to remove this from the public API, so my thought is to modify NonRootNode's constructor behavior to call this for each of the parents. Specifically, I want to write:
abstract class NonRootNode(override val parents: List[Hierarchical[_]])
extends Hierarchical[NonRootNode] {
//for each parent, add `this` to its children
parents.map{p=>p.addChild(this)}
}
However, when I add this line, I get the following error:
Error:(19, 29) type mismatch;
found : NonRootNode
required: Hierarchical[_$3]
parents.map{p=>p.addChild(this)}
I'm not entirely sure why I'm getting this compiler error. My understanding of Hierarchical[_] is any Hierarchical, but I could be mistaken. In any case, I think I'm close to my desired behavior. What am I doing wrong?
Hierarchical[_] does NOT mean "any Hierarchical". It means "a Hierarchical of some fixed type, that is unknown".
For example
def foo: Hierarchical[_] = new Hierarchical[Int]
works: it declares a function, that will return some implementation of Hierarchical, exact type of which is not known to the caller. This is fine.
On the other hand:
def bar(h: Hierarchical[String]) = doStuff(h)
bar(foo)
Does not work: function bar wants a parameter of an exact type Hierarchical[String], and what is passed to it can not be guaranteed to have that type, it's type parameter is unknown.
In your case, .addChild is a method of Hierarchical[T] (where the value of T is unknown to you), and wants a parameter of the same type . But what you are passing to it is Hierarchical[NonRootNode]. This is illegal, because there is no way to guarantee that NonRootNode will be the same as the (unknown) T.

Type-parametrized type hierarchies

I was wondering if anyone has any experience with creating a type-parametrized type hierarchy? I am fairly certain this is acheivable as a result of scala's pseudo-unification of packages & static objects.
The specific use case I have in mind is parametrizing an id type over an application framework so you can use your choice of int/long/java.util.UUID/BSONId/whatever. Consider as a rough example:
package foolib.generic
trait GenEntity[I] { def id: I }
trait GenRepository[I] { def getById(id: I): GenEntity[I] }
trait FooTaxonomy[I] {
type Entity = GenEntity[I]
type Repository = GenRepository[I]
object subpackage extends generic.subpackage.SubpackageTaxonomy[I]
}
You would then configure the hierarchy for use in a project with something like:
package object myprj {
object foolib extends foolib.generic.FooTaxonomy[java.util.UUID]
// Whee!
val someEntity = new myprj.foolib.Entity(java.util.UUID.randomUUID())
}
Are there any reasons this is a spectacularly bad idea? Any pitfalls/etc I should be aware of?
This approach would work but you may encounter problems when the number of type parameters increases. Perhaps a solution would be to use abstract type members instead of type parameters.
Another approach is to use the cake pattern which I think provides a better solution in your case. The exact logic of your code eludes me a bit, so this rewrite may not entirely represent your intention:
package foolib.generic
//defines common types used by all modules
trait CoreModule {
type Id // abstract type, not commited to any particular implementation
}
//module defining the EntityModule trait
trait EntityModule { this: CoreModule => //specifying core module as a dependency
trait GenEntity {
def id: Id
}
def mkEntity(id: Id): Entity //abstract way of creating an entity
}
//defines the GenRepository trait
trait RepositoryModule { this: EntityModule with CoreModule => //multiple dependencies
trait GenRepository {
def getById(id: Id): GenEntity
}
val repository: GenRepository //abstract way of obtaining a repository
}
//concrete implementation for entity
trait EntityImplModule extends EntityModule { this: CoreModule =>
case class Entity(val id: Id) extends GenEntity
def mkEntity(id: Id) = Entity(id)
}
//modules that provides a concrete implementation for GenRepository
trait RepositoryImplModule extends RepositoryModule { this: CoreModule with EntityModule =>
object RepositoryImpl extends GenRepository {
def getById(id: Id) = mkEntity(id)
}
}
//this unifies all your modules. You can also bind any dependencies and specify any types
object Universe
extends CoreModule
with EntityImplModule
with RepositoryImplModule {
type Id = java.util.UUID
val repository = RepositoryImpl
def mkEntity(id: Id) = Entity(id)
}
//usage
object Main {
import Universe._
import java.util.UUID
val entity = repository.getById(UUID.randomUUID())
println(entity.id)
}
This achieves your goal of creating an implementation independent of the concrete type Id and it also provides a nice way to do dependency injection.
The modules which provide a concrete implementation for GenRepository, for instance, may require a concrete type for Id. You can very well create another module which binds Id to a concrete type and make the RepositoryImplModule depend on the former module, thus specifying that this concrete implementation of GenRepository will work only for a certain type of ids.
The Cake pattern is very powerful and has many variations. This video explains it quite well, I recommend you watch it if you are interested in this solution:
Cake Pattern: The Bakery from the Black Lagoon

Is scala's cake pattern possible with parametrized components?

Parametrized components work well with the cake pattern as long as you are only interested in a unique component for each typed component's, example:
trait AComponent[T] {
val a:A[T]
class A[T](implicit mf:Manifest[T]) {
println(mf)
}
}
class App extends AComponent[Int] {
val a = new A[Int]()
}
new App
Now my application requires me to inject an A[Int] and an A[String], obviously scala's type system doesn't allow me to extends AComponent twice. What is the common practice in this situation ?
I think the AComponent doesn't need to be parameterized itself. So loose the type parameter and change this into
trait AComponent {
val aInt: A[Int]
val aStr: A[String]
class A[T](implicit mf:Manifest[T]) {
println(mf)
}
}
class App extends AComponent {
val aInt = new A[Int]()
val aStr = new A[String]()
}
if you want to be able to provide instances for Int and String

Seamless weaving of trait

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.