Pattern matching with class type does not seem to work?


#1
sealed trait NodeType{
  def && (anotherNode : NodeType) : NodeType = (this, anotherNode) match {
    case (a, b) if a == b => a
    case (a : PureType, b : RightNodeType) => PureType
    case _ => this
  }
}
object PureType extends NodeType
object RightNodeType extends NodeType
object LeftNodeType extends NodeType

The 2nd case statement gives the compile time error Cannot resolve symbol PureType and RightNodeType

But the following works

sealed trait NodeType{
def && (anotherNode : NodeType) : NodeType = (this, anotherNode) match {
case (a, b) if a == b => a
case (a, b) if a== PureType && b == RightNodeType => PureType
case _ => this
}
}
object PureType extends NodeType
object RightNodeType extends NodeType
object LeftNodeType extends NodeType


#2

It seems you’ve pasted the same (non-working) code twice.


#3

Thanks for pointing it out. I have edited and corrected the question


#4

object Xxx defines a value named Xxx. It’s type is Xxx.type. Conceptually object Xxx extends Yyy with Zzz {} is very similar to lazy val Xxx = new Yyy with Zzz {}. Also, uppercase identifiers in patterns are treated as constants to match against.

Top level lazy vals are not allowed, so I’ll wrap NodeType subtypes in object NodeType. After that adjustment following code still doesn’t compile:

package questions

import questions.NodeType._

sealed trait NodeType {
  def &&(anotherNode: NodeType): NodeType = (this, anotherNode) match {
    case (a, b) if a == b                => a
    case (a: PureType, b: RightNodeType) => PureType
    case _                               => this
  }
}
object NodeType {
  object PureType      extends NodeType
  object RightNodeType extends NodeType
  object LeftNodeType  extends NodeType
}

That is because PureType and RightNodeType are values, not types, If PureType was a type, then PureType wouldn’t be allowed to be used as a value at the right side of a case. You need to add .type suffix to extract type from value. Therefore the code will look like that:

package questions

import questions.NodeType._

sealed trait NodeType {
  def &&(anotherNode: NodeType): NodeType = (this, anotherNode) match {
    case (a, b) if a == b                          => a
    case (a: PureType.type, b: RightNodeType.type) => PureType
    case _                                         => this
  }
}
object NodeType {
  object PureType      extends NodeType
  object RightNodeType extends NodeType
  object LeftNodeType  extends NodeType
}

If we change object to lazy val then the && method still compile fine (and probably would work correctly as I’ve assigned new anonymous class to every lazy val):

package questions

import questions.NodeType._

sealed trait NodeType {
  def &&(anotherNode: NodeType): NodeType = (this, anotherNode) match {
    case (a, b) if a == b                          => a
    case (a: PureType.type, b: RightNodeType.type) => PureType
    case _                                         => this
  }
}
object NodeType {
  lazy val PureType      = new NodeType {}
  lazy val RightNodeType = new NodeType {}
  lazy val LeftNodeType  = new NodeType {}
}

Note that code below compiles because PureType and RightNodeType were used as values (they are values, so that’s OK) during comparison.

package questions

import questions.NodeType._

sealed trait NodeType {
  def &&(anotherNode: NodeType): NodeType = (this, anotherNode) match {
    case (a, b) if a == b                              => a
    case (a, b) if a == PureType && b == RightNodeType => PureType
    case _                                             => this
  }
}
object NodeType {
  object PureType      extends NodeType
  object RightNodeType extends NodeType
  object LeftNodeType  extends NodeType
}

You can use the fact that uppercase identifiers are treated as constants and remove explicit comparisons ending with following code:

package questions

import questions.NodeType._

sealed trait NodeType {
  def &&(anotherNode: NodeType): NodeType = (this, anotherNode) match {
    case (a, b) if a == b          => a
    case (PureType, RightNodeType) => PureType
    case _                         => this
  }
}
object NodeType {
  object PureType      extends NodeType
  object RightNodeType extends NodeType
  object LeftNodeType  extends NodeType
}

As before, you can replace object with anonymous class assigned to lazy val and code will work the same way.


#5

I don’t see the difference between the two versions.

The compiler expects types, not objects, in these positions. PureType and RightNodeType are objects, not types. Their types are PureType.type and RightNodeType.type.

And I’m wondering what you are trying to achieve - doesn’t the match expression always return this?