How to access fields from instances of abstract class


#1

I have an abstract class named Bdd

sealed abstract class Bdd (ident:Int) {
}

I’m using a sealed class in hopes of getting warnings on non-exhaustive pattern matching.

I’ve included (ident:Int) in the initialization arguments because all the subclasses have such an initialization argument.

I’m trying to define an object of the same name Bdd (perhaps that is mistake?)
But in this situation, IntelliJ complains of errors in the call to hash.get(...), and the compiler gives a similar error.

50

If I change the declaration from sealed abstract class Bdd to sealed abstract case class, then the type mismatch goes away, but I get a compiler error which InteliJ was not able to predict.

Error:(116, 12) case class BddNode has case ancestor Bdd, but case-to-case inheritance is prohibited. To overcome this limitation, use extractors to pattern match on non-leaf nodes.
case class BddNode(label:Int, positive:Bdd, negative:Bdd) extends Bdd(Bdd.nextCount()) {

I think there is some fundamental concept I’m missing. Since, I’ve declared positive and negative of type Bdd, why doesn’t it know that positive.ident is an Int?

Here is the actual code.

object Bdd {
  var count = 2
  def nextCount():Int = {
    count += 1
    count - 1
  }
  val hash = mutable.Map.empty[(Int,Int,Int),BddNode]

  def bdd(label:Int):Bdd = {
    bdd(label,BddTrue,BddFalse)
  }
  def bdd(label:Int,positive:Bdd,negative:Bdd):Bdd = {
    if (positive eq negative)
      positive
    else {
      // Type mismatch: expected: (Int,Int,Int) actual: (Int,Any,Any)
      hash.get((label, positive.ident, negative.ident)) match {
        case Some(bdd: BddNode) => bdd
        case None => {
          val bdd = BddNode(label,positive,negative)
          hash((label,positive.ident,negative.ident)) = bdd
          bdd
        }
      }
    }
  }

#2

Eventually I may have answered my own question, as to how to make ident visible.
I’ve changed the Bdd class definition to the following:

sealed abstract class Bdd (val ident:Int) {
}

After doing that, the compiler can now resolve the reverences to positive.ident and negative.ident within the definition of Bdd.bdd.

However, I don’t understand the semantics.
When the superclass Bdd is defined as

sealed abstract class Bdd (ident:Int) { // missing the val
}

… but all the subclasses are defined as follows, what is the meaning of ident in the Bdd definition?

case class BddNode(label:Int, positive:Bdd, negative:Bdd) extends Bdd(Bdd.nextCount()) {
...
}

sealed abstract class BddTerm(ident:Int) extends Bdd(ident) {
}

object BddTrue extends BddTerm(1) {
  ...
}

object BddFalse extends BddTerm(0) {
  ...
}

#3

Without the val, it is a simple constructor parameter, visible only inside the class body (which is also the primary constructor’s body in Scala). So if you don’t refer to it there, it will not be used anywhere.

Case classes are special in this regard, they make all their constructor parameters into vals without explicitly declaring them as such. That’s why using a case class fixed the same error as using a val (albeit causing a different one).