Scala 101

Disclaimer

  • I'm no Scala guru
  • This is a quick crash course
  • Stop me whenever you feel lost

Quick overview

  • Created in 2004
  • Java++
  • Functional + OO
  • Designed to scale
  • Popular

Agenda

Values and variables

val/var name: Type = Value
val value: String = "Hello"
var variable: String = "Confoo"
  • Value : Immutable and can be only assigned once
  • Variable : Mutable, re-assignable and discouraged
// Case classes
case class Foo(s1: String, s2: String)
val foo = Foo("Hello", "World")

Foo("Hello", "World") == Foo("Hello", "World") // returns true
// Traits
trait Behavior1 { def doSomething() = /*...*/ }
trait Behavior2 { def doSomethingElse() = /*...*/ }

case class Foo extends Behavior1 with Behavior2
/* ... with BehaviorN */
// Objects
object Bar {
  def newBarWithFalse = new Bar(false)
}
class Bar(val foo: Boolean)
/*...*/
Bar.newBarWithFalse // returns Bar(false)
// Common classes
class Foo(val s1: String, var s2: String = "bar")
val foo = new Foo(s1 = "Hello World")
// Case classes
case class Foo(s1: String, s2: String)
val foo = Foo("Hello", "World")

Foo("Hello", "World") == Foo("Hello", "World") // returns true
// Traits
trait Behavior1 { def doSomething() = /*...*/ }
trait Behavior2 { def doSomethingElse() = /*...*/ }

case class Foo extends Behavior1 with Behavior2
/* ... with BehaviorN */
// Objects
object Bar {
  def newBarWithFalse = new Bar(false)
}
class Bar(val foo: Boolean)
/*...*/
Bar.newBarWithFalse // returns Bar(false)
// Common classes
class Foo(val s1: String, var s2: String = "bar")
val foo = new Foo(s1 = "Hello World")
// Case classes
case class Foo(s1: String, s2: String)
val foo = Foo("Hello", "World")

Foo("Hello", "World") == Foo("Hello", "World") // returns true
// Traits
trait Behavior1 { def doSomething() = /*...*/ }
trait Behavior2 { def doSomethingElse() = /*...*/ }

case class Foo extends Behavior1 with Behavior2
/* ... with BehaviorN */
// Objects
object Bar {
  def newBarWithFalse = new Bar(false)
}
class Bar(val foo: Boolean)
/*...*/
Bar.newBarWithFalse // returns Bar(false)
// Common classes
class Foo(val s1: String, var s2: String = "bar")
val foo = new Foo(s1 = "Hello World")
// Case classes
case class Foo(s1: String, s2: String)
val foo = Foo("Hello", "World")

Foo("Hello", "World") == Foo("Hello", "World") // returns true
// Traits
trait Behavior1 { def doSomething() = /*...*/ }
trait Behavior2 { def doSomethingElse() = /*...*/ }

case class Foo extends Behavior1 with Behavior2
/* ... with BehaviorN */
// Objects
object Bar {
  def newBarWithFalse = new Bar(false)
}
class Bar(val foo: Boolean)
/*...*/
Bar.newBarWithFalse // returns Bar(false)
// Common classes
class Foo(val s1: String, var s2: String = "bar")
val foo = new Foo(s1 = "Hello World")

Functions

def name(arg: Type,...): Returned_Type = body
def substract(x:Int, y:Int): Int = { x - y }
def printHelloWorld: Unit = println("hello world !")
def foo(): Int = {
                        def bar(): Int = { ... }
                    }
Anonymous
val name: (arg type,...) => Returned Type = body
val multiply: (Int, Int) => Int = (x,y) => x * y
def name(arg: Type,...): Returned_Type = body
def substract(x:Int, y:Int): Int = { x - y }
def printHelloWorld: Unit = println("hello world !")
def foo(): Int = {
  def bar(): Int = { ... }
}
Anonymous
val name: (arg type,...) => Returned Type = body
val multiply: (Int, Int) => Int = (x,y) => x * y
def name(arg: Type,...): Returned_Type = body
def substract(x:Int, y:Int): Int = { x - y }
def printHelloWorld: Unit = println("hello world !")
def foo(): Int = {
  def bar(): Int = { ... }
}
Anonymous
val name: (arg type,...) => Returned Type = body
val multiply: (Int, Int) => Int = (x,y) => x * y

Higher Order Functions

val func: (arg type,...) => Returned Type = body
val multiply: (Int, Int) => Int = (x,y) => x * y
val foo: Int => Int = x => x + 1
def bar(f: Int => Int, number: Int ): Int = f(number)
bar(foo, 41) // returns 42
def bar(f: Int => Int): (Int, Int) => Int = (x, y) => f(x + y)
bar(x => x * 2)(2, 3) // returns 10
Multiple-argument-list function
def reduce(combine: (Int, Int) => Int)
          (x: Int, y: Int): Int = combine(x, y)
val sum: (Int, Int) => Int = reduce((x,y) => x + y)
sum(1, 2) // returns 3
val func: (arg type,...) => Returned Type = body
val multiply: (Int, Int) => Int = (x,y) => x * y
val foo: Int => Int = x => x + 1
def bar(f: Int => Int, number: Int ): Int = f(number)
bar(foo, 41) // returns 42
def bar(f: Int => Int): (Int, Int) => Int = (x, y) => f(x + y)
bar(x => x * 2)(2, 3) // returns 10
Multiple-argument-list function
def reduce(combine: (Int, Int) => Int)
          (x: Int, y: Int): Int = combine(x, y)
val sum: (Int, Int) => Int = reduce((x,y) => x + y)
sum(1, 2) // returns 3
val func: (arg type,...) => Returned Type = body
val multiply: (Int, Int) => Int = (x,y) => x * y
val foo: Int => Int = x => x + 1
def bar(f: Int => Int, number: Int ): Int = f(number)
bar(foo, 41) // returns 42
def bar(f: Int => Int): (Int, Int) => Int = (x, y) => f(x + y)
bar(x => x * 2)(2, 3) // returns 10
Multiple-argument-list function
def reduce(combine: (Int, Int) => Int)
          (x: Int, y: Int): Int = combine(x, y)
val sum: (Int, Int) => Int = reduce((x,y) => x + y)
sum(1, 2) // returns 3

Type inference

val integer: Int = 1
val integer = 1
def getData = { "hello world !" }
def printHelloWorld = { println("hello world !") }
val multiply = (x:Int, y:Int) => x * y
val list = List(1,2,3)
list.filter(int => int > 1)
list.filter(_ > 1) // returns List(2,3)
def reduce(f: (Int, Int) => Int)(x:Int, y:Int) = f(x,y)
reduce((a,b) => a + b)(1, 2) // a and b's type are inferred
reduce(_ + _)(1, 2)
val integer: Int = 1
val integer = 1
def getData = { "hello world !" }
def printHelloWorld = { println("hello world !") }
val multiply = (x:Int, y:Int) => x * y
val list = List(1,2,3)
list.filter(int => int > 1)
list.filter(_ > 1) // returns List(2,3)
def reduce(f: (Int, Int) => Int)(x:Int, y:Int) = f(x,y)
reduce((a,b) => a + b)(1, 2) // a and b's type are inferred
reduce(_ + _)(1, 2)
val integer: Int = 1
val integer = 1
def getData = { "hello world !" }
def printHelloWorld = { println("hello world !") }
val multiply = (x:Int, y:Int) => x * y
val list = List(1,2,3)
list.filter(int => int > 1)
list.filter(_ > 1) // returns List(2,3)
def reduce(f: (Int, Int) => Int)(x:Int, y:Int) = f(x,y)
reduce((a,b) => a + b)(1, 2) // a and b's type are inferred
reduce(_ + _)(1, 2)

Error handling and prevention

val foo: Option[String] = Option("ok") // returns Some("ok")
val bar: Option[String] = Option(null) // returns None
val foo: Try[String] = Try { /* exception */ } // returns Failure(ex)
val bar: Try[String] = Try { "ok" } // returns Success("ok")
val optionX = Option(42) // Some(42)
optionX.filter(_ > 45) // None
optionX.filter(_ > 40) // Some(42)
val dangerousInt = Try(1/0) // Failure(java.lang.ArithmeticException)
                    dangerousInt.map(_ + 42) // Failure(java.lang.ArithmeticException)
dangerousInt.getOrElse(-1)
                    dangerousInt.get // throws an exception if equal to None or Failure
val foo: Option[String] = Option("ok") // returns Some("ok")
val bar: Option[String] = Option(null) // returns None
val foo: Try[String] = Try { /* exception */ } // returns Failure(ex)
val bar: Try[String] = Try { "ok" } // returns Success("ok")
val optionX = Option(42) // Some(42)
                        optionX.filter(_ > 45) // None
                        optionX.filter(_ > 40) // Some(42)
val dangerousInt = Try(1/0) // Failure(java.lang.ArithmeticException)
                        dangerousInt.map(_ + 42) // Failure(java.lang.ArithmeticException)
dangerousInt.getOrElse(-1)
                        dangerousInt.get // throws an exception if equal to None or Failure
val foo: Option[String] = Option("ok") // returns Some("ok")
val bar: Option[String] = Option(null) // returns None
val foo: Try[String] = Try { /* exception */ } // returns Failure(ex)
val bar: Try[String] = Try { "ok" } // returns Success("ok")
val optionX = Option(42)
optionX.filter(_ > 45) // None
optionX.filter(_ > 40) // Some(42)
val dangerousInt = Try(1/0)
dangerousInt.map(_ + 42) // Failure(java.lang.ArithmeticException)
dangerousInt.getOrElse(-1)
                        dangerousInt.get // throws an exception if equal to None or Failure
val foo: Option[String] = Option("ok") // returns Some("ok")
val bar: Option[String] = Option(null) // returns None
val foo: Try[String] = Try { /* exception */ } // returns Failure(ex)
val bar: Try[String] = Try { "ok" } // returns Success("ok")
val optionX = Option(42)
optionX.filter(_ > 45) // None
optionX.filter(_ > 40) // Some(42)
val dangerousInt = Try(1/0)
dangerousInt.map(_ + 42) // Failure(java.lang.ArithmeticException)
dangerousInt.getOrElse(-1) // Returns -1 if value cannot be found
dangerousInt.get // throws an exception if equal to None or Failure
val foo: Option[String] = Option("ok") // returns Some("ok")
val bar: Option[String] = Option(null) // returns None
val foo: Try[String] = Try { /* exception */ } // returns Failure(ex)
val bar: Try[String] = Try { "ok" } // returns Success("ok")
val optionX = Option(42)
optionX.filter(_ > 45) // None
optionX.filter(_ > 40) // Some(42)
val dangerousInt = Try(1/0)
dangerousInt.map(_ + 42) // Failure(java.lang.ArithmeticException)
dangerousInt.getOrElse(-1) // Returns -1 if value cannot be found
dangerousInt.get // throws an exception if equal to None or Failure

Pattern Matching

e match {
case pattern [guard] => case_block
/* more cases */
case _ => // default case
}
e match {
case pattern [guard] => case_block
/* more cases */
case _ => // default case
}
case class Foo(s: String, i: Int)
Foo("Hello Confoo", 10) match { case Foo(_, i) if integer > 0 => print("Foo with positive i") case Foo(s, _) => print("Foo with non-positive i") case _ => println("foo is not a Foo") }
e match {
case pattern [guard] => case_block
/* more cases */
case _ => // default case
}
case class Foo(s: String, i: Int)
Foo("Hello Confoo", 10) match { case Foo(_, i) if integer > 0 => print("Foo with positive i") case Foo(s, _) => print("Foo with non-positive i") case _ => println("foo is not a Foo") }
e match {
case head :: tail => println("this is a list !")
case (x, y) => println(s"this is a tuple with values $x and $y !")
case Some(value) => println(s"this is an option containing $value")
/* ... */
}
Advanced features and Patterns.

Implicits

case class Foo(string: String)
def bar()(implicit foo: Foo) = println(foo.string)
implicit val implicitFoo = Foo("Hello Confoo")
bar() // prints "Hello Confoo"
object Foo { implicit val defaultFoo = Foo("Default message") }
bar() // prints "Default message"
// Type conversion
case class Foo(s: String)
implicit def fooToInt(foo:Foo): Int = foo.s.toInt
val i: Int = Foo("42") // returns 42
// Class extension
implicit case class RichInt(int: Int) {
  def doSomething() = /*...*/
}
/*...*/
42.doSomething()
package object v1 { implicit val v1Foo = Foo("Foo v1") }
package object v2 { implicit val v2Foo = Foo("Foo v2") }
// ...
def run(v1IsEnabled:Boolean): Unit = {
  if(v1IsEnabled) { import v1._; bar() }
  else            { import v2._; bar() }
}
def compare[A](x: A, y: A)(implicit order: Ordering[A]) = order.compare(x, y)
def compare[A : Ordering](x: A, y: A) = implicitly[Ordering[A]].compare(x, y)
def compare[A](x: A, y: A)(implicit order: Ordering[A]) = order.compare(x, y)
def compare[A : Ordering](x: A, y: A) = implicitly[Ordering[A]].compare(x, y)
// Implicit evidence
trait TraversableOnce[+A] {
// [A] must be a tuple in order to call this method
def toMap[T, U](implicit ev: <:< [A, (T, U)]): immutable.Map[T, U]
}
def print[T](value: T): Unit
def print[T](value: T): Unit = value match {
  case s:String => println(s"This is a string %value")
  case i:Int => println(s"This is an Int %value")
  /*...*/
}
trait Printer[T] { def print(value: T): Unit }

def print[T: Printer](value: T): Unit = implicitly[Printer[T]].print(value)
object Printer {
  implicit object StringPrinter extends Printer[String] {
    def print(string: String): Unit = println(s"This is a string %string")
  }

  implicit object IntPrinter extends Printer[Int] {
    def print(int: Int): Unit = println(s"This is an Int %int")
  }
  /*...*/
}
trait Printer[T] { def print(value: T): Unit }

def print[T: Printer](value: T): Unit = implicitly[Printer[T]].print(value)
object Printer {
  implicit object StringPrinter extends Printer[String] {
    def print(string: String): Unit = println(s"This is a string %string")
  }

  implicit object IntPrinter extends Printer[Int] {
    def print(int: Int): Unit = println(s"This is an Int %int")
  }
  /*...*/
}
  • Boilerplate code reduction
  • Transparent adapters and decorators
  • New Behaviors Introduction (Type classes)

Stackable Trait Pattern

class IntQueue(ints: List[Int] = List.empty) {
  def push(int: Int) = new IntQueue(int :: ints)
}
class PositiveIntQueue extends IntQueue {
  override def push(int: Int) = if(int > 0) super.push(int) else this
}
class DoubledIntQueue extends IntQueue {
  override def push(int: Int) = super.push(int * 2)
}
class IntQueue(ints: List[Int] = List.empty) {
  def push(int: Int) = new IntQueue(int :: ints)
}
class PositiveIntQueue extends IntQueue {
  override def push(int: Int) = if(int > 0) super.push(int) else this
}
class DoubledIntQueue extends IntQueue {
  override def push(int: Int) = super.push(int * 2)
}
trait Doubling extends IntQueue {
  override def push(int: Int) = super.push(int * 2)
}

trait Incrementing extends IntQueue {
  override def push(int: Int) = super.push(int + 1)
}

trait Filtering extends IntQueue {
  override def push(int: Int) = if(int > 0) super.push(int) else this
}
val queue = new IntQueue with Doubling with Incrementing with Filtering
queue.push(1).push(2).push(0) // returns IntQueue(6, 4)
val queue = new IntQueue with Doubling with Filtering with Incrementing
queue.push(1).push(2).push(0) // returns IntQueue(4, 6, 4)

What's left ?

  • Scala's core : Projections, Variance, structural-types, higher-kinded types, lambda types, Cake-Pattern, Collections, Metaprogramming...
  • Concurrency : Future, Promises, Actors (Akka)
  • Functional Programming : Categories, Functors, Monads, lazy evaluation, Scalaz...
  • ...

Conclusion

  • Programming Scala : by Dean Wampler and Alex Payne
  • Functionnal Programming in Scala : by Paul Chiusano and RĂșnar Bjarnason
  • Scala for the Impatient : by Cay Horstmann
I am a Scala developer at 1Science, but also a mobile dev freelancer. francistoth@coding-hipster.com
Thank you ! Questions ? http://www.coding-hipster.com/presentations.html