Polymorphism in Scala, Introduction to Graphs

Polymorphism in Scala

In Python we explored the concept of polymorphism. We can implement the same method in different classes, then call that method without worrying about which implementation we’re calling.

What would happen if we tried to do something like this in Scala? We can define another class for DJ-ing–maybe one used at a different radio station, with different policies on prizes and song requests:

class AlternativeDJData {

  private var queue: List[String] = List()

  def request(caller: String, song: String): String = {
    queue = song :: queue
    "Great, we'll play it next"
  }

  def play(): String = {
    val song = queue.head
    queue = queue.tail
    song
  }
}

Now let’s say we want to write a method that can use either of these classes. We should be able to do this: after all, they both implement the request and play methods! We might write something like this:

object Caller extends App {
  def callIn(djd: TYPE): Unit = {
    println(djd.request("Doug", "Stairway to Heaven"))
    println(djd.play())
  }

  callIn(new RockDJData("Alex"))
  callIn(new AlternativeDJData())
}

What should we fill in as the type of djd? If we put RockDJData, we won’t be able to use the method with AlternativeDJData. If we put AlternativeDJData we won’t be able to use it with RockDJData. And if we write another class–maybe JazzDJData–we’ll have more problems.

We want to be able to put something as the type of djd that tells Scala: anything we pass as the argument to this method will implement the request and play methods. We can do this:

trait DJData {
  def request(caller: String, song: String): String
  def play(): String
}

// edit class definitions
class RockDJData extends DJData
class AlternativeDJData extends DJData

Then we can edit our definition of callIn:

object Caller extends App {
  def callIn(djd: DJData): Unit = {
    println(djd.request("Doug", "Stairway to Heaven"))
    println(djd.play())
  }

  callIn(new RockDJData("Alex"))
  callIn(new AlternativeDJData())
}

Introduction to graphs

Here are a few scenarios, each specifying data we might want to track and access in a computer program. The scenarios are all related, though it may be difficult to see this at first!

  • A social networking application tracks friendships between people.
  • A railroad company tracks which of its stations are connected, and what the distances between them are
  • A gossip blog tracks which musical artists have recorded diss tracks targeting which other individuals.

As an exercise, try drawing diagrams representing each of these scenarios. What do your diagrams have in common? What are the differences?

Each of these situations can be represented by a graph. A graph consists of:

nodes
a node represents a single piece of data, for instance a person (in the case of the social network or the gossip blog) or a railroad station. You will sometimes see nodes referred to as “vertices” (the plural of “vertex”).
edges
edges connect pairs of nodes. For instance, in the social network an edge is a friendship.

Nodes almost always store data. Edges can also store data–for instance, the distance between two railroad stations, or maybe the title of a diss track.

Graphs can be either undirected or directed. In an undirected graph, edges simply connect pairs of nodes. Edges in undirected graphs are usually drawn as lines. In a directed graph, each edge connects a source node and a destination node; in other words, edges have a direction. Edges in directed graphs are usually drawn as arrows between the source and the destination.

The social network and the railroad are probably undirected graphs (although not all social networks are undirected–what about Twitter?). The gossip blog needs a directed graph–an artist might have recorded a diss track targeting someone who hasn’t recorded a diss track of their own!