title: Replacing case class inheritance with extractors preserving exhaustiveness checks in Scala
body: <p>I have a simple class hierarchy that represents a graph-like structure with several distinct types of vertexes implemented using case classes:</p>
  
  <pre><code>sealed trait Node
  
  sealed abstract case class Vertex extends Node
  case class Arc extends Node
  
  case class VertexType1 (val a:Int) extends Vertex
  case class VertexType2 (val b:Int) extends Vertex
  </code></pre>
  
  <p>This allows me to write match blocks like this:</p>
  
  <pre><code>def test (x: Node) = x match {
    case _ : Arc =&gt; "got arc"
    case _ : Vertex =&gt; "got vertex"
  }
  </code></pre>
  
  <p>or like this:</p>
  
  <pre><code>def test (x: Node) = x match {
    case _ : Arc =&gt; "got arc"
    case c : Vertex =&gt; c match {
      case _ : VertexType1(a) =&gt; "got type 1 vertex " + a
      case _ : VertexType2(a) =&gt; "got type 2 vertex " + a
    }
  }
  </code></pre>
  
  <p>Note that this implementation has the following properties:</p>
  
  <p>1) It allows writing match blocks that differentiate between arcs and vertices, but not between specific vertex types, but also match blocks that do differentiate between vertex types.</p>
  
  <p>2) In both vertex-type-specific and non-vertex-type-specific match blocks the exhaustiveness of pattern matching is checked.</p>
  
  <p>However, inheritance from case classes is deprecated, and the compiler suggests to use extractors instead to support matching on non-leaf nodes (i.e., in the above example, to differentiate between arcs and vertices, but not between vertex types). </p>
  
  <p>The question: is it possible to implement a similar class hierarchy without using case class inheritance, but still having pattern exhaustiveness checks performed by the compiler in both use cases shown above?</p>
  
  <p><strong>EDIT</strong>: I have added a constructor parameter to the VertexType classes so that the match is not performed only on types.</p>
  
  <p>My current implementation without the case classes is as follows:</p>
  
  <pre><code>sealed trait Node
  
  sealed abstract class Vertex extends Node
  class Arc extends Node
  
  class VertexType1 (val a:Int) extends Vertex
  class VertexType2 (val b:Int) extends Vertex
  
  object VertexType1 {
    def unapply (x : VertexType1) : Some[Int] = Some(x.a)
  }
  
  object VertexType2 {
    def unapply (x : VertexType2) : Some[Int] = Some(x.b)
  }
  </code></pre>
  
  <p>And the test code:</p>
  
  <pre><code>def test (x: Node) = x match {
    case _ : Arc =&gt; "got arc" 
    case v : Vertex =&gt; v match {
      case VertexType1(a) =&gt; "got vertex type 1 " + a 
    }
  }
  </code></pre>
  
  <p>I expect a warning about non-exhaustive match in the second block (VertexType2 is never matched), but there isn't one.</p>
  
  <p><em>Actually, Scala compilers before 2.9.0-RC3 produce a warning that I expect to see, but versions starting with RC3 (including 2.9.0 and 2.9.0-1) do not, which is rather confusing.</em></p>