Data structures and classes in Scala

Data structures

We saw several built-in data structures in Python: lists, hashtables, and sets. Scala has analogues of all three:

scala> val lyrics = List("Happy", "birthday", "to")
scala> val songs = Map("Beatles" -> List("Yellow Sub", "Help"), "Britney" -> List("Toxic", "Oops"))
scala> val genres = Set("country", "rock", "rap")
scala> lyrics(2)
"to"
scala> songs("Beatles")(1)
"Help"
scala> genres.contains("bluegrass")
false
scala> genres("bluegrass") // same thing as above!
false

All three work pretty much like their counterparts in Python, with one big difference: all three are immutable. This means that once we’ve built a List, Map, or Set, we can’t change its contents! So we can do operations like this:

scala> lyrics :+ "you"
List("Happy", "birthday", "to", "you")
scala> songs :+ ("Cardi B" -> List("I Like It"))
Map("Beatles" -> List("Yellow Sub", "Help"), "Britney" -> List("Toxic", "Oops"),
    "Cardi B" -> List("I Like It"))
scala> genres + "bluegrass"
Set("country", "rock", "rap", "bluegrass")

But we can’t do operations like this:

scala> lyrics(0) = "Sad"
ERROR
scala> songs("Beatles") = List("Sgt Pepper")
ERROR
scala> genres.add("folk")
ERROR

In general, when you’re programming in Scala you’ll use these immutable versions of data structures. However, Scala does also have mutable data structures:

scala> val lyrics = Array("Twinkle", "twinkle", "little", "star")
scala> lyrics(3) = "fish"
scala> val numbers = scala.collection.mutable.Map("one" -> 1, "two" -> 2)
scala> numbers("three") = 3
scala> numbers("three")
3
scala> val cities = scala.collection.mutable.Set("Cranston", "Central Falls")
scala> towns.add("Newport")

var, val, mutability, and immutability

Did you notice something surprising about that last code block? I said that val means you can’t change the value of a name, but we sure seem to be changing lyrics, numbers, and cities! What’s going on? Can we think about what this means in terms of the program dictionary and memory?

If I define a name x as a val, I can’t change the value of x in the program dictionary. But if x is mapped to a location in memory–say, loc 6–I can change loc 6 (for instance, adding an entry to a mutable Map) without changing the entry for x in the program dictionary!

See the lecture capture for more details about this distinction.

Classes and objects in Scala

Let’s translate one more program from Python to Scala.

class DJData:
  def __init__(self, dj_name: str):
    self.dj_name = dj_name
    self.num_callers = 0
    self.queue = []

  def request(self, caller: str, song: str) -> str:
    self.queue.append(song)
    self.num_callers += 1
    if self.num_callers % 1000 == 0:
      return "Congrats, " + caller + "! You get a prize from " + dj_name + "!"
    else:
      return "Cool, " + caller

We can translate this to Scala as follows:

class DJData(val djName: String) {

  var numCallers: Int = 0
  var queue: List[String] = List()

  def request(caller: String, song: String): String = {
    queue = queue :+ song
    numCallers += 1
    if (numCallers % 1000 == 0) {
      "Congrats, " + caller + "! You get a prize from " + djName + "!"
    } else {
      "Cool, " + caller
    }
  }
}

There are a few important differences above:

  • In Scala, we don’t write out a class’s constructor (i.e., __init__ in Python). Instead, we put any fields we want to initialize from outside the class into the first line.
  • Methods don’t take a self argument
  • Inside a method, we don’t need to write self.queue: we can just write queue.

We can make an instance of this class and experiment with its functionality in the REPL:

scala> val djd = new DJData("DJ Doug")
scala> djd.request("Alex", "Stairway to Heaven")
"Cool, Alex"
scala> djd.queue
List("Stairway to Heaven")