I have recently been going through the book "Scala by Example" in which the author creates an abstract class to represent a set of ints "IntSet" with two subclasses (EmptySet and NonEmptySet) as follows:
abstract class Stack[A] {
def push(x: A): Stack[A] = new NonEmptyStack[A](x, this)
def isEmpty: Boolean
def top: A
def pop: Stack[A]
}
class EmptyStack[A] extends Stack[A] {
def isEmpty = true
def top = error("EmptyStack.top")
def pop = error("EmptyStack.pop")
}
class NonEmptyStack[A](elem: A, rest: Stack[A]) extends Stack[A] {
def isEmpty = false
def top = elem
def pop = rest
}
My question is this: How useful is this paradigm of representing an empty container as its own class instead of creating one concrete class to handle both the empty and non-empty cases?
Each implementation is simpler and more readable as the is-empty-check does not have to be done in the implementation. This leads to better code metric values (like cyclomatic complexity) too.
Moreover, It makes the implementations slightly faster in general, since the distinction between empty and non-empty does not have to be done at runtime. As far as I know, Scala's
Set
applies this technique and implements different types of sets (used depending on their size) to optimize performance.Obviously this works for immutable data structures only.