1

I have a class and two case subclasses:

abstract class C
case class C1(left: C, right: C, weight: Int) extends C
case class C2(weight: Int) extends C

I want to implement something like the following:

def weight(t: C): Int = {
    t match {
      case c1: C1 => l.weight
      case c2: C2 => c2.left.weight + c1.right.weight //this line doesn't let the code compile
    }
  }

The above code does not compile. says left and right do not have attribute weight. I am seeing this because C does not have a weight defined for it. Only c1 and c2 do. However, the class instances that I pass to the weight function would be either c1 or c2 and would definitely have weight.

It is actually a recursive weight summation on a tree that I can achieve with the following:

def weight(t: C): Int = {
    t match {
      case c1: C1 => l.weight
      case c2: C2 => weight(c2.left) + weight(c1.left)
    }
  }

But, I do not want to recurse and I do not think I should have to if the weight information is simply there available in the instance that I am passing in.

It is an assignment question that I am trying to solve and the signature of classes C, C1 and C2 are sacrosanct. I have tried modifying

abstract class C

to

abstract class C:
    val weight: Int = ???

However, this then starts raising issues with the weight variable in the C1 and C2 signatures and asks me to override it.

The one solution I tried and thought would work was creating a companion object:

abstract class CodeTree
object CodeTree:
  val weight: Int = ???
case class Fork(left: CodeTree, right: CodeTree, chars: List[Char], weight: Int) extends CodeTree
case class Leaf(char: Char, weight: Int) extends CodeTree

But I think the companion objects are not inherited by the subclasses

  • How do I achieve the desired outcome without having to recurse?
  • More broadly, How do I endow an abstract class with -- this particular field would always be available in the subclasses that inherit from me and hence it should be available statically with me and not fail compilation?
Dmytro Mitin
  • 48,194
  • 3
  • 28
  • 66
figs_and_nuts
  • 4,870
  • 2
  • 31
  • 56

2 Answers2

2

You should add member weight: Int to C

abstract class C:
  def weight: Int
case class C1(left: C, right: C, weight: Int) extends C
case class C2(weight: Int) extends C

Then you can match typed patterns

def weight(t: C): Int =
  t match
    case c1: C1 => c1.left.weight + c1.right.weight
    case c2: C2 => c2.weight

or constructor patterns

def weight(t: C): Int = 
  t match 
    case C1(l, r, _) => l.weight + r.weight
    case C2(w)       => w

Why do we have a need for separate case identifiers in case of type only matching in scala?

Normally abstract members are defs and overriden with def/val/lazy val (var if necessary) in implementations.

Dmytro Mitin
  • 48,194
  • 3
  • 28
  • 66
1

Adding weight to the superclass is the proper approach. But then you actually have to define weight in C1, which results in your not needing the pattern matching to begin with.

abstract class C { def weight: Int }
case class C1(left: C, right: C) extends C { def weight = left.weight + right.weight } 
case class C2(weight: Int) extends C

def weight(t: C) = t.weight
Dima
  • 39,570
  • 6
  • 44
  • 70