multi-paradigm: object-oriented, functional, imperative

Pedro Chambino
Abr 24, 2012

http://p.chambino.com/slides/scala

History

Scala is a relatively recent language.

2001
design started by Martin Odersky at the École Polytechnique Fédérale de Lausanne (Switzerland)
late 2003 - early 2004
released on the Java platform (and on the .NET platform in June 2004)
March 2006
version 2.0 released (current version 2.9.2)
17 January 2011
the Scala team won a 5 year research grant of over €2.3 million from the European Research Council

Hello World

# hello.scala
object HelloWorld {
  def main(args: Array[String]) {
    println("Hello, world!")
  }
}

or

# hello.scala
object HelloWorld extends App {
  println("Hello, world!")
}
  > scalac hello.scala           # compile it
  > scala HelloWorld             # execute it

Hello World

# hello.scala
println("Hello, world!")
  > scala hello.scala             # script it

Read-Eval-Print Loop

  > scala                         # interactive shell
  scala> 1+1
  res0: Int = 2

POJO

Java

class Person {
    private String firstName;
    private String lastName;
    private int    age;

    public Person(String firstName, String lastName, int age) {
        this.firstName = firstName;
        this.lastName  = lastName;
        this.age       = age;
    }

    public void setFirstName(String firstName) { this.firstName = firstName; }
    public void String getFirstName() { return this.firstName; }
    public void setLastName(String lastName) { this.lastName = lastName; }
    public void String getLastName() { return this.lastName; }
    public void setAge(int age) { this.age = age; }
    public void int getAge() { return this.age; }
}

Scala

class Person(var firstName: String, var lastName: String, var age: Int)

Interaction with Java

import java.util.{Date, Locale}
import java.text.DateFormat
import java.text.DateFormat._

object FrenchDate {
  def main(args: Array[String]) {
    val now = new Date
    val df = getDateInstance(LONG, Locale.FRANCE)
    println(df format now)
  }
}
Powerful imports
  • Import multiple classes from same package with curly braces
  • Import wildcard is _ instead of * because * is a valid scala identifier
  • Also imports are relative!
Syntactic Sugar
  • Methods with zero or one argument can use the infix syntax:
    • df format now equals df.format(now)
    • new Date equals new Date()

Variable Declaration and Inferring Type Information

val x = 0
var y = 1
var z: Any = 2
x = 3                     // error: reassignment to val
y = 4
y = "5"                   // error: type mismatch
z = "6"
lazy val l = println("!!!")

Inferring Type Information and Generics

Java

Map<Integer, String> intToStringMap = new HashMap<Integer, String>();

Scala

val intToStringMap: Map[Int, String] = new HashMap
val intToStringMap2 = new HashMap[Int, String]

Everything is an object

  1 + 2 * 3 / x

consists of method calls and is equivalent to:

  (1).+(((2).*(3))./(x))

this means that +, -, * and / are valid identifiers in Scala

I call it my billion-dollar mistake. It was the invention of the null reference in 1965. (...) I couldn't resist the temptation to put in a null reference, simply because it was so easy to implement. (...)
Tony Hoare

Option, Some, and None: Avoiding nulls

val stateCapitals = Map(
  "Alabama" -> "Montgomery",
  "Wyoming" -> "Cheyenne")

println( "Alabama: " + stateCapitals.get("Alabama") )
println( "Unknown: " + stateCapitals.get("Unknown") )
println( "Alabama: " + stateCapitals.get("Alabama").get )
println( "Wyoming: " + stateCapitals.get("Wyoming").getOrElse("Oops!") )
println( "Unknown: " + stateCapitals.get("Unknown").getOrElse("Oops2!") )

// *** Outputs ***
// Alabama: Some(Montgomery)
// Unknown: None
// Alabama: Montgomery
// Wyoming: Cheyenne
// Unknown: Oops2!

A possible implementation of get that could be used by a concrete subclass of Map:

def get(key: A): Option[B] = {
  if (contains(key))
    new Some(getValue(key))
  else
    None
}

Functions are objects too

object Timer {
  def oncePerSecond(callback: () => Unit) {
    while (true) { callback(); Thread sleep 1000 }
  }

  def timeFlies() {
    println("time flies like an arrow...")
  }

  def main(args: Array[String]) {
    oncePerSecond(timeFlies)
    // or
    oncePerSecond(() =>
      println("time flies like an arrow..."))
  }
}

Method Default and Named Arguments

def joiner(strings: List[String], separator: String = " "): String =
    strings.mkString(separator)

println(joiner(List("Programming", "Scala")))
println(joiner(strings = List("Programming", "Scala")))
println(joiner(List("Programming", "Scala"), " "))
println(joiner(List("Programming", "Scala"), separator = " "))
println(joiner(strings = List("Programming", "Scala"), separator = " "))

Currying

def concat(s1: String)(s2: String) = s1 + s2
// alternative syntax:
// def concat(s1: String) = (s2: String) => s1 + s2

val hello = concat("Hello ")(_)
println(hello("World")) // => Hello World

// transforms a normal function into a curried function:
def concat2(s1: String, s2: String) = s1 + s2
val curriedConcat2 = Function.curried(concat2 _)

Scala for comprehensions

val dogBreeds = List("Doberman", "Yorkshire Terrier", "Dachshund",
                     "Scottish Terrier", "Great Dane", "Portuguese Water Dog")

for (breed <- dogBreeds)
  println(breed)

// *** Filtering ***

for (
  breed <- dogBreeds
  if breed.contains("Terrier");
  if !breed.startsWith("Yorkshire")
) println(breed)

// *** Yielding ***

val filteredBreeds = for {
  breed <- dogBreeds
  if breed.contains("Terrier")
  if !breed.startsWith("Yorkshire")
  upcasedBreed = breed.toUpperCase()
} yield upcasedBreed

No break or continue!

Mixins

trait Observer[S] {
  def receiveUpdate(subject: S);
}

trait Subject[S] {
  this: S =>
  private var observers: List[Observer[S]] = Nil
  def addObserver(observer: Observer[S]) = observers = observer :: observers

  def notifyObservers() = observers.foreach(_.receiveUpdate(this))
}

Using Traits

class Account(initialBalance: Double) {
  private var currentBalance = initialBalance
  def balance = currentBalance
  def deposit(amount: Double)  = currentBalance += amount
  def withdraw(amount: Double) = currentBalance -= amount
}

class ObservedAccount(initialBalance: Double)
  extends Account(initialBalance)
  with Subject[Account]
{
  override def deposit(amount: Double) = {
    super.deposit(amount)
    notifyObservers()
  }
  override def withdraw(amount: Double) = {
    super.withdraw(amount)
    notifyObservers()
  }
}

class AccountReporter extends Observer[Account] {
    def receiveUpdate(account: Account) =
        println("Observed balance change: "+account.balance)
}

Case Classes

// Represent expressions like: (x+x)+(7+y)
abstract class Tree
case class Sum(l: Tree, r: Tree) extends Tree
case class Var(n: String) extends Tree
case class Const(v: Int) extends Tree

Case Classes and Pattern Matching

type Environment = String => Int

def eval(t: Tree, env: Environment): Int = t match {
  case Sum(l, r) => eval(l, env) + eval(r, env)
//case l Sum r => eval(l, env) + eval(r, env) // alternative syntax
  case Var(n) => env(n)
  case Const(v) => v
}

Pattern Matching

def countScalas(list: List[String]): Int = {
  list match {
    case "Scala" :: tail => countScalas(tail) + 1
    case _ :: tail       => countScalas(tail)
    case Nil             => 0
  }
}
val langs = List("Scala", "Java", "C++", "Scala", "Python", "Ruby")
val count = countScalas(langs)
println(count)    // => 2

Matching on Type

val sundries = List(23, "Hello", 8.5, 'q')

for (sundry <- sundries) {
  sundry match {
    case n: Int if (n > 0) => println("got a Natural: " + n)
    case i: Int => println("got an Integer: " + i)
    case s: String => println("got a String: " + s)
    case f: Double => println("got a Double: " + f)
    case other => println("got something else: " + other)
  }
}

Actor Model of Concurrency

import scala.actors.Actor._
val echoActor = actor {
  loop {
    receive {
      case msg => println("received: "+msg)
    }
  }
}
echoActor ! "hello"
echoActor ! "world!"

XML

val name = "Bob"

val bobXML =
<person>                /// <person>
  <name>{name}</name>   ///   <name>Bob</name>
</person>               /// </person>

bobXML \ "name"        // => <name>Bob</name>
bobXML \ "name" text   // => Bob
(bobXML \ "name").text // => Bob

Summary

Who uses Scala?

Foursquare
uses Lift (web application framework) and Scala
Twitter
backend was converted from Ruby to Scala for performance benefits
LinkedIn
uses Scalatra (micro web application framework) to power its Signal API
The Guardian (newspaper's website guardian.co.uk)
switched from Java to Scala

And much more...

Bibliography