1

I'm trying to make "trait bundles" that I can mix together, which mix traits contained within them. The following approach doesn't seem to work. Can you suggest an approach that can work?

trait GraphTypes {
  trait Node {
    def size: Double
  }
}

trait HasBigAndSmallNodes extends GraphTypes {
  trait BigNode extends super.Node {
    override def size = 5.00
  }

  trait SmallNode extends super.Node {
    override def size = 0.05
  }
}

trait NodesHaveHappiness extends GraphTypes {
  trait Node extends super.Node {
    def happiness = size * 2.0  // Ideally, .happiness would mix in
  }                             // to BigNode and SmallNode.
}

object ACertainGraph extends GraphTypes
  with NodesHaveHappiness with HasBigAndSmallNodes
{
  val big = new BigNode { }
  val small = new SmallNode { }

  println(big.happiness)  // error: value happiness is not a member of Graph.BigNode
}

This doesn't work, I think because there's nothing that says that BigNode and SmallNode extend NodesHaveHappiness.Node (but see this question). I don't want to write that in explicitly, though. I want to be able to put node types into separate "trait bundles" from "trait bundles" that define node attributes, and mix them together (somehow) so that all the node types get all the node attributes.

How do you keep this manageable in Scala?

Community
  • 1
  • 1
Ben Kovitz
  • 4,920
  • 1
  • 22
  • 50

1 Answers1

0

You are trying to intermix inheritance with composition in weird ways. The fact that you put a bunch of inner traits on a trait does not mean that inheriting the outer will make objects inherit the inner. Also, making Graph inherit some trait does nothing to the object you define inside Graph. You are instead saying that a Graph IS A Node, which is weird.

You probably want to build a class inheritance that makes sense to you, and then you have to "bundle" them together when you actually instantiate things. Here is a varied example:

trait Node

class SizedNode(val size: Double)

trait HappyNode extends SizedNode { // Here you specify that a happy node requires a size, so happines is well defined.
  def happiness = size * 2.0
}

trait SmartNode extends Node {
  def saySomethingSmart() = println("E = mc^2")
}

object Graph {
  def createBigNode() = new SizedNode(5.0) with HappyNode with SmartNode  // Here you can "bundle" as many traits as you want.
  def createSmallNode() = new SizedNode(0.05) with HappyNode
}

object Test {

  def main(args: Array[String]) {

    println(Graph.createBigNode().happiness)  // 10.0
    println(Graph.createSmallNode.happiness)  // 0.1
    Graph.createBigNode().saySomethingSmart() // E = mc^2
  }
}
Daniel Langdon
  • 5,899
  • 4
  • 28
  • 48
  • Sorry, my question must have been very unclear. I'm looking for a way to say "In this graph, there are such-and-such node types, and they should all get such-and-such attributes" without explicitly re-listing all the node types and spelling out that each node type gets each node attribute in each graph. So if I made a trait that defined `.happiness` differently, I should be able to make a graph with it without writing a new `def createBigNode() = new...with DifferentHappyNode...` for each node type. I'm asking for any approach that works, though; it doesn't have to work like my example. – Ben Kovitz Nov 28 '15 at 16:46
  • BTW, the Graph object “isn’t a” Node, it “has” Node, as in [§2.3 of this paper](http://lampwww.epfl.ch/~odersky/papers/ScalableComponent.pdf), summarized in [this answer](http://stackoverflow.com/a/25829204/1393162). I'll try to edit my question to make a it a little clearer. – Ben Kovitz Nov 28 '15 at 16:48
  • I don't think you can get away with it. For any kind of graph that you define, you need to provide at the very least something that creates nodes with the properties you want for it. The relevant trait could be in the companion object for sure. To figure out what to instantiate based on inner clases would require ugly refleccion... – Daniel Langdon Nov 28 '15 at 23:44