Your browser doesn't support the features required by impress.js, so you are presented with a simplified version of this presentation.

For the best experience please use the latest Chrome, Safari or Firefox browser.

Scala Beginner Workshop

net-a-porter.github.io/scala-beginner-workshop

What is Scala?

Why Scala?

Agenda

vals and vars

vals and vars

Examples

val title = "Jurassic Park"

val runtime = 12345

val is3d = false

val role = ("Sam Neil", "Dr. Alan Grant")

var rating = 0.8
rating = 0.9
        

Task

Examples

val title: String = "Jurassic Park"

val runtime: Int = 12345

val is3d: Boolean = false

val role: (String, String) = ("Sam Neil", "Dr. Alan Grant")

var rating: Double = 0.8
rating = 0.9
methods

def (method)

def success(budget: Int, gross: Int) = 
  budget < gross

val r = success(100, 200)
r == true
        

def (method)

def success(budget: Int, 
            gross: Int): Boolean = 
  budget < gross

val r = success(100, 200)
r == true
        

def (method)

def success(budget: Int, 
            gross: Int) = {
  budget < gross
}

val r = success(100, 200)
r == true
        

Task

def (method)

def simpleRating(rating: Double) =
  if(rating > 8.0) "Excellent"
  else if(rating > 4.0) "Okay"
  else "Poor"

val s = simpleRating(5.0)
s == Okay
        

Default Parameters

def success(budget: Int, 
            gross: Int = 50) = 
  budget < gross

val r = success(100)
r == false

val r = success(100, 200)
r == true
        

By Name Parameters

def success(budget: Int = 50, 
            gross: Int = 50) = 
  budget < gross

val r = success(budget = 100)
r == false
        
classes

Classes



class Actor(firstName: String,
            surname: String)

val tom = new Actor("Tom", "Hanks")
        

Classes



class Actor(firstName: String,
            surname: String)

val tom = new Actor("Tom", "Hanks")
tom.name
        

Classes


class Actor(firstName: String, 
            surname: String) {
    val name = firstName + " " + surname
}

val tom = new Actor("Tom", "Hanks")
p.name
        

Classes


class Actor(firstName: String, 
            surname: String) {
    def name = firstName + " " + surname
}

val tom = new Actor("Tom", "Hanks")
p.name
        

Case Classes

case class Actor(firstName: String,
                 surname: String)

Case Class - Copy

val tom = new Actor("Tom", "Hanks")

val tom2 = tom.copy(surname = "Cruise")

tom2.surname == "Cruise"

Case Class - Pattern Match

val m = Movie(certificate = "U")

val ageRequirement = p match {
  case Movie("U" | "PG") =>   0
  case Movie("12A") =>        18
  case Movie(cert) =>         cert.toInt
}

Task

the AND operator is &&

Operators are methods

case class Actor(name: String) {
  def +(other: Actor) =
    Actor(name + other.name)
}

Actor("Brad").+(Actor("Angelina"))
Actor("Brad") +(Actor("Angelina"))
Actor("Brad") + Actor("Angelina")
Functions

Functions



(args) => body

val funky = (args) => body

Functions

val success = (budget: Int, gross: Int) => 
  budget < gross

val r = success(100, 200)

val r = success.apply(100, 200)

Task

case class Movie(name: String, rating: Float)
val kb = Movie("Kill Bill", rating = 8.2)
val tr = Movie("The Room", rating = 3.5)
> greatFilm(kb) == true
> greatFilm(tr) == false

Write the function greatFilm. It checks the rating is >= 7.0

Task

val success = 
  (budget: Int, gross: Int) => budget < gross

val success: (Int, Int) => Boolean = 
  (budget, gross) => budget < gross

val success: (Int, Int) => Boolean =
  _ < _
Collections and Options

Collections

Collections

val movies = List(
  Movie("Jurassic Park", 10.0f), 
  Movie("The Room", 3.5f), 
  Movie("Home Alone", 7.4f)
)

movies: List[Movie] = List(Movie(...

Collections

val movies = 
  Movie("Jurassic Park", 10.0f) ::
  Movie("The Room", 3.5f) ::
  Movie("Home Alone", 7.4f) :: Nil

movies: List[Movie] = List(Movie(...

Collections

movies.count(greatFilm)
2

        
movies.count(_.name.length > 10)
1

Collections

movies.filter(greatFilm)
List(
  Movie(Jurassic Park,10.0), 
  Movie(Home Alone,7.4))

movies.filter(_.name.contains("J"))
List(Movie(Jurassic Park,10.0))

Collections

movies.map(greatFilm)
List(true, false, true)

        
movies.map(_.name)
res: List[String] = List(
  Jurassic Park, 
  The Room, 
  Home Alone)

Collections

movies.collect {
  case Movie(name, rating) if rating < 4 => name
}
res: List[String] = List(The Room)

Collections

val actor = List("Tom", "Val", "Anthony")
val chars = List("Maverick", "Iceman", "Goose")
actor zip chars
res15: List[(String, String)] = List(
  (Tom,Maverick), 
  (Val,Iceman), 
  (Anthony,Goose))

Task

case class Movie(genre: String, gross: Int)

val movies = (100 to 5000 by 50).flatMap(g =>
  Movie("Drama", g) :: Movie("Romcom", g) :: 
  Movie("Action", g) :: Nil)

Some answers

val res = movies.
  filter(_.genre == "Action").
  map(_.gross).
  sum

val res = movies.collect { 
  case Movie("Action", gross) => gross
}.sum

Functions again

movies.map (movie => 
  if(movie.score < 4) "Flop" 
  else "Blockbuster"
)

movies.collect {
  case movie if movie.score < 4 => "Flop"
}

Functions again

movies.map (movie => 
  if(movie.score < 4) "Flop" 
  else "Blockbuster"
)

movies.collect {
  case movie if movie.score < 4 => "Flop"
  case _ => "Blockbuster"
}

Functions again

movies.map (movie => 
  if(movie.score < 4) "Flop" 
  else "Blockbuster"
)

movies.map {
  case movie if movie.score < 4 => "Flop"
  case _ => "Blockbuster"
}

Partial Functions

val f: Int => String = {
  case 0 => "zero!" 
  case 1 => "one!"
}

What happens if we call this with the argument 3?

Options

Options

val sort = Some("newest")

val sort = None

val sort = Option("newest")

val sort = Option(null)

Options

 val sort: Option[String] = …

sort.getOrElse("newest")

Options

val querySort: Option[String] = …
val headerSort: Option[String] = …

> val token = querySort.orElse(headerSort)
Some("newest")

Options

val sort: Option[String] = …

val movies = sort
  .map(sort => getMoviesFromDb(sort))

page = Some(List(Movie(...), Movie(...)))
page = None

Task

Rating(3, None).asString
"5* [no comment]"

Rating(1, Some("So awful")).asString
"1* [comment: So awful]"

Some Solutions

def asString = 
  stars + "* " + 
  comment.map("[comment:" + _ + "]").
  getOrElse("[no comment]")

def asString = 
  stars + "* " + (
  if(comment.nonEmpty) "[comment:" + comment.get + "]" 
  else "[no comment]")

def asString = stars + "* " + (comment match { 
  case Some(c) => s"[comment:$c]" 
  case None => "[no comment]" 
})

Callback Hell


val spielbergCast = movies.collect {

  case Movie("Spielberg", cast) =>
    cast.map(_.name)

}
.flatten

For Expressions

val spielbergCast = for {
  movie <- movies
  if movie.director.name == "Spielberg"
  actor <- movie.cast
} yield actor.name
Traits

Traits

Traits

case class Award(name: String, 
                 outcome: String)
// outcome is "Won" or "Nominated"

trait Person {
  def awards: List[Award]
  def awardsWon = 
    prods.filter(_.outcome == "Won")
}

Traits

case class Actor(awards: List[Award])
    extends Person

val seanBean = Actor(
  Award("Best Lead Actor", "Won") :: Nil
)

seanBean.awardsWon.size == 1

Traits

case class Director(awards: List[Award])

val spielberg = new Director(
  Award("Best Film", "Won") :: Nil
) with Person

spielberg.awardsWon.size == 0

Traits

Traits

trait Production { def releaseYear: Int }
case class Film(releaseYear: Int) extends Production

case class TvShow(series: List[Series]) { 
  def releaseYear = series.sortBy(_.year).head.year 
}
val thirtyRock = new TvShow(
  Series(2011) :: Series(2012) :: Nil
) with Production

thirtyRock.releaseYear
res2: Int = 2011
        
Objects

Objects

object Counties {
  val countries = "GB" :: "ZH" :: Nil

  def supported(c: String) =
    countries.contains(c)
}

> Countries.supported("ZH")
> true

Objects

case class Movie(name: String)

object Movie {
  def apply(name: String) =
    new Movie(name)
}

> Movie("Jurassic Park")

Objects as enums

sealed trait Outcome
case object Nominated extends Outcome
case object Won extends Outcome

Objects as enums


case class Award(outcome: Outcome)
val award = Award(Won)

val msg = award match {
    case Award(Won) => "Woohoo!"
    case Award(_)   => "Unlucky"
}
Implicits

Implicits

Implicit conversions

case class HttpRequest (
  headers: List[HttpHeader]
)

case class HttpHeader (
  name: String, value: String
)

Implicit conversions

implicit class PimpedHttpRequest(r: HttpRequest) {
  def headerValue(name: String) = 
    r.headers.find(_.name == name)
}

val request = HttpRequest(…)
request.headerValue("page")

// Compiler generates
// PimpedHttpRequest(request).headerValue("page")

Task

case class Duration(mins: Int, secs: Int)
        

Duration(1, 30).totalSecs == 90

An Answer

implicit class PimpedDuration(d: Duration) {
  def totalSecs = d.mins * 60 + d.secs
}

Duration(1, 30).totalSecs == 90

Implicit Parameters

Seq(22, 2, 11, 1).sorted
// Seq(1, 2, 11, 22)

Seq("22", "2", "11", "1").sorted
// Seq(1, 11, 2, 22)

Implicit Parameters

trait Seq[A] {
  def sorted[A](implicit ord: Ordering[A]): Seq[A]
}

Implicit Parameters

case class Film(year: Int)

implicit val filmOrd = new Ordering[Film] {
  def compare(x: Film, y: Film) = 
    x.year.compareTo(y.year)
}

val films = Seq(Film(2011), Film(1960), Film(1994))
films.sorted
// List(Film(1960), Film(1994), Film(2011))

Implicit Parameters

trait Seq[A] {
 def sorted[A](asc: Boolean)
              (implicit ord: Ordering[A]): Seq[A]
}
val films = Seq(Film(2011), Film(1960), Film(1994))
films.sorted(false)

Implicit Parameters

implicit val movieOrd = new Ordering[Film] {
  def compare(x: Film, y: Film) = 
    x.year.compareTo(y.year)
}

implicit object FilmOrd extends Ordering[Film] {
  def compare(x: Film, y: Film) = 
    x.year.compareTo(y.year)
}

Other topics