0

Say we have two objects, A and B, that have a parent-child relationship. Something like:

    A.child = B

Is there a function or some way to implement a function in Scala that would work like so:

    B.getParent() //returns A
tharindu_DG
  • 8,900
  • 6
  • 52
  • 64
jaaj
  • 17
  • 1
  • 1
  • 4
  • Yes there is but you need to write one. TBH this question does not make much sense at all. Can you please give some more detail? – jhegedus Nov 02 '17 at 20:05

2 Answers2

1

Scala has no built-in capability to manage such relationships.

However, it's trivial to add a parent member to B's constructor and to populate it as follows:

class A {
  val child = new B(this)
  // ...
}

class B(val parent: A) {
  // ...
}

// Some possible actions:
val a = new A
val b = a.child
a == b.parent // Should be true

In this particular case, A is also responsible for constructing B, which allows both A and B to be immutable.

There's a huge number of variations on this theme.

A more general approach, which allows instances of A and B to be less tightly coupled, is to use external maps to track the relationship. The following is an example of how this can be done in a functional way:

class A {
  // ...
}

class B {
  // ...
}

// Constructor is private to require construction through companion's factory method.
class Relationships private(private val parentToChild: Map[A, B], private val childToParent: Map[B, A]) {

  // Return new relationship that adds relationship between child and parent instance.
  //
  // This particular approach assumes a one-to-one mapping (one child per parent).
  // One-to-many (multiple children per parent) are possible by mapping the parent to a
  // collection of children.
  def relate(parent: A, child: B): Relationships = {

    // Verify that neither parent nor child currently have relationships.
    require(!parentToChild.contains(parent) && !childToParent.contains(child))

    // Add the relationship, return new relationships instance.
    val newParentToChild = parentToChild + (parent -> child)
    val newChildToParent = childToParent + (child -> parent)
    new Relationships(newParentToChild, newChildToParent)
  }

  // Lookup child of particular parent. Return None if no child found, Some(child) otherwise.
  def childOf(parent: A): Option[B] = parentToChild.get(parent)

  // Lookup parent of particular child. Return None if no parent found, Some(parent) otherwise.
  def parentOf(child: B): Option[A] = childToParent.get(child)
}

// Companion.
object Relationships {

  // Create initial relationship instance with no relationships.
  def apply() = new Relationships(Map.empty[A, B], Map.empty[B, A])
}

// Sample use:
val a = new A
val b = new B
val tracker = Relationships().relate(a, b)
tracker.childOf(a) // Should return Some(b).
tracker.parentOf(b) // Should return Some(a).
Mike Allen
  • 8,139
  • 2
  • 24
  • 46
0

One solution would be to use by-name parameters and lazy val as follows...

class TreeNode[E](l: => Option[TreeNode[E]], value: E, r: => Option[TreeNode[E]], p: => Option[TreeNode[E]]) {

  lazy val left: Option[TreeNode[E]] = l

  lazy val right: Option[TreeNode[E]] = r

  lazy val parent: Option[TreeNode[E]] = p

}

// must explicitly define the types for left and right!
val left: TreeNode[Int] = new TreeNode(None, 6, None, Some(root))
val right: TreeNode[Int] = new TreeNode(None, 15, None, Some(root))
val root = new TreeNode(Some(left), 10, Some(right), None)

Note that by-name parameters are not allowed in case classes.

davidrpugh
  • 4,363
  • 5
  • 32
  • 46