Declaring Values with Type Inference
val number: Int = 5
val name: String = "Joe"
Could be re-written as
val number = 5
val name = "Joe"
EVERYTHING is an Object
5.toString // res0: String = 5
And I mean everything
val add: (Int, Int) => Int = _ + _
add.toString // res1: String = <function2>
And... Everything is an Expression
val resultString = if(wasSuccessful) "Success" else "Failed"
val otherResultString = try {
doSomething
"Success"
} catch {
e: Exception => "Failed"
}
val list: Seq[String] = for (i <- 1 to 10) yield s"Hello $i"
Plays Nicely with the Host Organism
import java.util.Random
def randomInt: Int = new Random().nextInt()
Symbolic Method Names
case class Duration(seconds: Int) {
override def toString: String = seconds + " seconds left"
def +(other: Duration) = Duration(seconds + other.seconds)
def -(other: Duration) = Duration(seconds - other.seconds)
}
In use
val d1 = Duration(10)
val d2 = Duration(7)
val result = d1 - d2 // same as d1.-(d2)
//result: Duration = 3 seconds left
}
Functional Programming Features
Higher Order Functions
case class User(name: String, age: Int)
val users: List[User] = User("Joe", 22) :: User("Bob", 43) ::
User("Kip", 56) :: Nil
// can also be written val toName: User => String = _.name
val toName: User => String = user => user.name
val userNames: List[String] = users.map(toName)
// more concise syntax
val userNames = users.map.(user => user.name)
Options
def getUserFromDb(id: Int): User = // DB stuff
val userMap = Map("Joe" -> 1, "Bob" -> 2, "Kip" -> 3)
val bobsId: Option[Int] = userMap.get("Bob")
// Some(Int) OR None
val bob: Option[User] = bobsId.map(getUserFromDb)
Pattern matching
val bob: Option[User] = ....
def printAge(user: Option[User]) = user match {
case None => println("No user here")
case Some(u) => println(s"The age is: ${u.age}")
}
Pattern Matching Continued
Find Bob and return his age
case class User(name: String, age: Int)
val users: List[User] = User("Joe", 22) :: User("Bob", 43) ::
User("Kip", 56) :: Nil
def getAge(name: String,
users: List[User]): Option[Int] = users match {
case Nil => None
case User(n, age) :: tail if n == name => Some(age)
case head :: tail => getAge(name, tail)
}
getAge("Bob", users)
//res0: Option[Int] = Some(43)
Collection Awesomeness
// get all users who are 25 years old or older
val canRentCars = users.filter(u => u.age >= 25)
// canRentCars: List[User] = List(User(Bob,43), User(Kip,56))
More Collection Awesomeness
// divide the list into two sub-lists as determined
// by whether the users are kids or adults
val (kids, adults) = users.partition(u => u.age < 18)
// kids: List[User] = List()
// adults: List[User] = List(User(Joe,22), User(Bob,43), User(Kip,56))
MOAR Collection Awesomeness
// partition, but what if the function is a
// big expensive operation???
val horriblySlowFunc = user => ...
val (l1, l2) = users.par.partition(horriblySlowFunc)
Find Bob and Return his Age REDUX
What we had before
case class User(name: String, age: Int)
val users: List[User] = User("Joe", 22) :: User("Bob", 43) ::
User("Kip", 56) :: Nil
def getAge(name: String,
users: List[User]): Option[Int] = users match {
case Nil => None
case User(n, age) :: tail if n == name => Some(age)
case head :: tail => getAge(name, tail)
}
getAge("Bob", users)
//res0: Option[Int] = Some(43)
Find Bob and Return his Age REDUX
But with Scala collections, you can easiliy do it in one line
case class User(name: String, age: Int)
val users: List[User] = User("Joe", 22) :: User("Bob", 43) ::
User("Kip", 56) :: Nil
def getAge(name: String,
users: List[User]): Option[Int] = {
users.find(u => u.name == name).map(u => u.age)
}
getAge("Bob", users)
//res0: Option[Int] = Some(43)