Runtime Polymorphism with Scala Extractors

Programming languages like Scala use strong typing to represent a model of the world explicitly in the source code. They move some of what would be semantic territory in a weakly typed language under the syntactic banner. But what about cases when your program encounters entities from the outside that need to be slotted into its type hierarchy? In Scala that’s where extractors come in.

Say you are writing a program that deals with two kinds of numbers: odd and even. You make an abstract Number class with implementations OddNumber and EvenNumber. Now suppose your program has to process human-readable text. In the string “There are at least 4 or 5 ways to skin a cat”, the distinctions between “4”, “5”, and “cat” are not obvious to a computer. You need a function that maps “4” to EvenNumber(4), “5” to OddNumber(5), and “cat” to none of the above. Here’s how you would do it in Scala.

import scala.util.Try

sealed trait Number {
  val x: Int
}

sealed trait NumberParser[N <: Number] {
  val remainder: Int

  def apply(x: Int): N

  def unapply(s: String): Option[N] =
    Try {s.toInt}.toOption
      .filter(_ % 2 == remainder)
      .map(apply(_))
}

case class EvenNumber private(x: Int) extends Number

object EvenNumber extends NumberParser[EvenNumber] {
  override val remainder = 0
}

case class OddNumber private(x: Int) extends Number

object OddNumber extends NumberParser[OddNumber] {
  override val remainder = 1
}

object Number {
  def unapply(s: String): Option[Number] = s match {
    case EvenNumber(n) => Some(n)
    case OddNumber(n) => Some(n)
    case _ => None
  }
}

This code provides the desired runtime behavior.

"4" match { case EvenNumber(_) => "Even number"; case _ => "Not an even number"}
// returns "Even number"
"5" match { case Number(_) => "Number"; case _ => "Not a number"}
// returns "Number"
"cat" match { case Number(_) => "Number"; case _ => "Not a number"}
// returns "Not a number"

It also does so with minimum of code repetition. The only difference between the even and odd number extractors (i.e. the unapply functions) is the value they compare to their mod 2 operation, and that is efficiently represented by a single overridden remainder value. There is a bit of non-DRYness in the way the tokens EvenNumber and OddNumber show up twice in their respective object definitions. It would be better not to have to provide an explicit type parameterization to the NumberParser mixin. I don’t know of a way to do this Scala, so I guess it’s not a perfect language.

I write this example mainly because I had to figure out how to do it recently and was only able to do so after some extensive help on Stackoverflow, so I thought I would make it easier on the next person. But it also nicely illustrates an aspect of programming syntax and semantics. That “4” is an even number is a semantic fact. That EvenNumber(4) is an even number is a syntactic fact. Scala extractors are a conduit between these two realms.

Advertisements
This entry was posted in Those that have just broken the flower vase. Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s