Cogs and Levers A blog full of technical stuff

Scala

Introduction

The Scala Programming Language is a language that brings together object oriented concepts with functional programming concepts on top of the jvm.

In today’s post, I’m going to go through some basic concepts of the language.

Classes

For our example, we’ll define a Player class. This will hold our player’s name, height and weight which won’t change once we set them. Note that we’re using the val keyword in the parameter list of the default constructor for the class. This automatically generates immutable members for us accessing this information.

class Player(val name: String, val height: Int, val weight: Int) {

  def getMessage(): String = "Game on!"

  def talk(): Unit = {
    val message = this.getMessage()
    println(s"$name the player says '$message'")
  }

}

We’ve also given our player the ability to talk. The player also has a message to say with getMessage.

Inheritance

We can inherit from this base Player class and define a Forward and a Back.

class Forward(name: String, height: Int, weight: Int) extends Player(name, height, weight) {
  override def getMessage(): String = "Uggg!"
}

class Back(name: String, height: Int, weight: Int) extends Player(name, height, weight) {
  override def getMessage(): String = "How does my hair look?"
}

Forwards and backs say different things, so we have overridden the default getMessage implementation in each case.

Traits

A trait is similar to the interface that you’d find in other languages. The main difference to a strict interface, is that a trait can have implementation. In the following example, the ValueEmitter trait is applied to different types of objects, but it commonly utilised to equate an answer.

trait ValueEmitter {
  def value(): Double
}

To represent a literal value and an operation both using this trait, we apply it to classes:

class LiteralValue(v: Double) extends ValueEmitter {
  def value(): Double = v
}

class Operation(val v1: ValueEmitter, val v2: ValueEmitter, val op: String) extends ValueEmitter {
  def value(): Double = {
    val left = v1.value()
    val right = v2.value()

    op match {
      case "+" => left + right
      case "-" => left - right
      case "*" => left * right
      case "/" => left / right
      case default => 0
    }
  }
}

Case Classes

Case classes allow you to concisely condense the definitions above:

abstract class ValueEmitter
case class LiteralValue(v: Double) extends ValueEmitter
case class Operation(v1: ValueEmitter, v2: ValueEmitter, op: String) extends ValueEmitter

This syncs up really well with the pattern matching ideas.

Pattern Matching

Following on with the example in Case Classes, we’ll write a function that uses pattern matching to ensure we’re getting the correct type through. Also see that we can pattern match on the values being passed through; not just the type.

def calculate(v: ValueEmitter): Double = v match {
  case LiteralValue(lv) => lv
  case Operation(v1, v2, "/") => {
    throw new Exception("I do not support divide")
  }
  case Operation(v1, v2, op) => {
    val left = calculate(v1)
    val right = calculate(v2)

    op match {
      case "+" => left + right
      case "-" => left - right
      case "*" => left * right
      case "/" => left / right
      case default => 0
    }

  }
}

Object

Static classes (or singleton objects) are just a way of defining single-use classes. You’ll see main sitting within an object definition rather than seeing main declared statically.

object Test {
  def main(args: Array[String]): Unit = {
    println("Hello, world")
  }
}

Another demonstrative use case for these objects is a configuration class:

object Config {
  def transactionDb(): String = "postgres://blah/xyz"
  def objectStoreDb(): String = "mongodb://quxx/abc"
}

Both transactionDb and objectStoreDb become accessible when prefixed with Config..

Accessors

You can short cut the creation of your accessors using your default constructor. As you’d expect, you use val for immutable, read-only properties and var for the read/write items.

class StockPrice(val code: String, var price: Double) {
}

The code on the stock doesn’t change but its price does.

These accessors can be defined manually using the following convention; this also allows you to specify any code that needs to execute within these accessors:

class StockPrice(val code: String, val initialPrice: Double) {

  private var _price: Double = initialPrice;

  def price: Double = _price
  def price_= (value: Double): Unit = {
    _price = value
  }

}

This is only a really small sample of the scala language, but will certainly get you up and running pretty quickly.

For more information, see the following links: