## Disclaimer
The content of these slides is a modified/adapted version of Twitter's Scala School project: http://twitter.github.io/scala_school/ https://github.com/twitter/scala_school2
Twitter is commited to training their new hires in Scala and we think that's cool. We also think that Scala is cool and we want you to think that as well.
Anyways, so credits goes to the guys at Twitter for building this material.
## Scala at Hopper
At hopper, we're building the largest structured database of travel information. We tackle complex problems at large scales so we like to have the right tools so we can focus on those problems.
* Started being used in 2011, only marginally/informally
* Became 50/50 (with Java) in 2012
* Hopper is officially a "Scala shop" in 2013
* 100% of our new backend code is Scala
* Java code being replaced as we replace components
We've replaced several complex core components. Everytime, the process cut down the amount of code by at least half.
## Scala at Hopper
Today, we use Scala everywhere
* Hadoop M/R
* Storm
* RPC services
* Web frontend tools (not the actual Web Application though)
* Data Analytics
* anywhere we can!
## About this class
The first session will cover basics: syntax and concepts. The second session will be more hands-on.
Today, we encourage you to follow along with your own interpreter so you can explore things on your own.
## Why Scala?
* Expressive
* First-class functions and closures
* Concise
* Type inference
* Literal syntax for function creation
* Java interoperability
* Can reuse java libraries and tools
* No performance penalty
* benefits from 15 years of JVM development and optimization
## What Scala?
Scala is a `sca`lable `la`nguage. It can scale as you need it to. This is Scala:
class Hello {
def greet(name : String) {
println("Hi there " + name)
}
}
And this is Scala
Write unit tests almost literally with Specs2
class HelloWorldSpec extends Specification {
"The 'Hello world' string" should {
"contain 11 characters" in {
"Hello world" must have size(11)
}
}
}
## And this is Scala
A word count Map/Reduce job using Scoobi
val lines = fromTextFile("hdfs://in/...")
val counts = lines.flatMap(_.split(" "))
.map(word => (word, 1))
.groupByKey
.combine(_+_)
persist(counts.toTextFile("hdfs://out/...", overwrite=true))
And this is Scala
Query MongoDB using Rogue (from Foursquare)
def update() = {
VenueRole where (_.venueid eqs this.id)
and (_.userid eqs userid)
modify (_.role_type setTo RoleType.manager)
upsertOne()
}
## And this is Scala
Write BASIC in Scala!
object SquareRoot extends Baysick {
def main(args: Array[String]) = {
10 PRINT "Enter a number"
20 INPUT 'n
30 PRINT "Square root of " % "'n is " % SQRT('n)
40 END
RUN
}
}
## How Scala?
* Compiles to java bytecode
* Works with any standard JVM
* Or even some non-standard JVMs like Dalvik
* Scala compiler written by author of Java compiler
## Think Scala
Although Scala is simply a jar file on your classpath, it is not just a library or a nicer/cleaner Java. It is its own language that happens to be compatible with all the java ecosystem. To properly learn Scala, approach it with a fresh mind.
## Start the Interpreter
Let's start the Scala REPL. Easiest way is to use `sbt`.
apt-get install sbt
or
brew install sbt
or
git clone https://github.com/twitter/finagle.git && cd finagle
Then start the `sbt console`.
$ sbt console
[...]
Welcome to Scala version 2.8.0.final (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_20).
Type in expressions to have them evaluated.
Type :help for more information.
scala>
# Variables and Expressions
## Mutable Variables
You can declare a variable `n` of type `Int` with initial value of `1 + 1` like so:
var n: Int = 1 + 1
You can reassign `var`
n = n + 1
But Scala has static typing, so you can't change a `var`'s type
var s: String = "Hi!"
n = s // doesn't compile
s = n // doesn't compile
## Statements and Expressions
We're all used to sequential execution of statements like this:
import util.Random.nextBoolean
var n: Int = 0
if (nextBoolean()) {
println("randomly true")
n = n + 1
} else {
println("randomly false")
n = n - 1
}
But in Scala, it is more conventional to use control-flow structures as expressions:
n = if(nextBoolean()) n + 1 else n - 1
and they compose
var s: String =
if (n % 2 == 0)
"n is even: %d, half is: %d".format(n, n / 2)
else
"n is odd: %d, randomly fixing it: %d".format(n, if (nextBoolean()) n + 1 else n - 1)
## Immutable Values
Given that expressions compose, mutable variables become less useful. It’s typically easiest to compute values by composing expressions rather than sequencing effects. So in Scala, we use `val`:
val n: Int = 1 + 1
n = n + 1 // doesn't compile
Sometimes `var` is necessary. Most of the time, it isn't.
## Equality
Scala has both reference and value equality.
val foo = "foobar"
val bar = new String("foobar")
foo == bar // true
foo.eq(bar) // false
## Functions
You can create functions with def.
scala> def addOne(m: Int): Int = m + 1
addOne: (m: Int)Int
In Scala, you need to specify the type signature for function parameters.
scala> val three = addOne(2)
three: Int = 3
## Zero-Argument Functions
You can leave off parens on functions with no arguments.
scala> def three() = 1 + 2
three: ()Int
scala> three()
res2: Int = 3
scala> three
res3: Int = 3
## Named and Default Arguments
When calling a method, you can pass arguments in an arbitrary order by naming them:
def formatUser(userId: Long, userName: String, realName: String): String =
"%s <%d>: %s".format(userName, userId, realName)
formatUser(
realName = "Dan Rosen",
userName = "drosen",
userId = 31337
)
When defining a method, you can assign default values to some or all of its arguments.
def formatUser(
userId: Long = 0,
userName: String = "unknown",
realName: String = "Unknown"
): String =
"%s <%d>: %s".format(userName, userId, realName)
formatUser(userName = "drosen")
## Side-effecting functions and `Unit`
Some functions are only used for their side-effects:
scala> println("Hi!")
Hi!
In C-influenced languages, the result type would be `void`, representing the absence of a value. But the notion that some functions can produce a value and some can't introduces inconsistenty in the language.
In order not to have this inconsistency, some languages, like Scala, introduce a type called `Unit` which has a single value `()`. So side-effecting functions can now return a value just like all the other ones:
def noop() : Unit = ()
Side-effecting functions can be written like so:
def noop() {
}
## Anonymous Functions
You can create anonymous functions.
scala> (x: Int) => x + 1
res2: (Int) => Int = <function1>
scala> res2(1)
res3: Int = 2
You can pass anonymous functions around or save them into vals.
scala> val addOne = (x: Int) => x + 1
addOne: (Int) => Int = <function1>
scala> addOne(1)
res4: Int = 2
## Big Functions
If your function is made up of many expressions, you can use {} to give yourself some breathing room.
def timesTwo(i: Int): Int = {
println("hello world")
i * 2
}
This is also true of an anonymous function.
val timesTwo = { i: Int =>
println("hello world")
i * 2
}
## Partial application
You can partially apply a function with an underscore, which gives you another function. Scala uses the underscore to mean different things in different contexts, but you can usually think of it as an unnamed magical wildcard. In the context of `{ _ + 2 }` it means an unnamed parameter. You can use it like so:
scala> def adder(m: Int, n: Int) = m + n
adder: (m: Int,n: Int)Int
scala> val add2 = adder(2, _:Int)
add2: (Int) => Int = <function1>
scala> add2(3)
res50: Int = 5
You can partially apply any argument in the argument list, not just the last one.
## Curried functions
Sometimes it makes sense to let people apply some arguments to your function now and others later.
Here's an example of a function with multiple argument lists:
scala> def multiply(m: Int)(n: Int): Int = m * n
multiply: (m: Int)(n: Int)Int
You can call it directly with both arguments, or fill in the first and then the second.
scala> multiply(2)(3)
res0: Int = 6
scala> val timesTwo = multiply(2) _
timesTwo: (Int) => Int = <function1>
scala> timesTwo(3)
res1: Int = 6
## Aside: the underscore `_`
Scala allows the use of the underscore as a wildcard in several places. It can be confusing when reading Scala at first.
As a rule of thumb, it either means _whatever_ or _everything_.
For example, using `_` within a single-argument function would mean _whatever the argument is_. Using it in an import statement would mean _everything from that package_.
## Variable length arguments
There is a special syntax for methods that can take parameters of a repeated type.
def countArgs(args: String*) = {
args.size()
}
scala> countArgs("One", "Two", "Three")
res0: Int = 3
## `while`- and `for`-loops
We've all written this code in _some_ language at least once:
var i = 0
while (i < 10) {
println("I'M AWESOME")
i = i + 1
}
or more succinctly:
for (i <- 0 until 10)
println("I'M AWESOME")
## `for`-loops
Maybe this is a bit more common:
import collection.mutable.Buffer
val quietWords = List("let's", "transform", "words")
val noisyWords = Buffer.empty[String]
for (i <- 0 until quietWords.size)
noisyWords += quietWords(i).toUpperCase
Although we're doing something very common, this code is extremely verbose (it's also terribly innefficient).
* `quietWords` appears 3 times
* `noisyWords` appears twice
So we can do better:
val noisyWords = Buffer.empty[String]
for (word <- quietWords)
noisyWords += word.toUpperCase
No more explicit indexing, and we're now linear time, but...
## `for`-expressions
The mutable `Buffer`-based approach is still dissatisfying. We've been doing very well treating control flow structures as value-producing expressions so far, so why not here?
val noisyWords =
for (word <- quietWords) yield
word.toUpperCase
Good! we're now refferring to `quierWords` and `noisyWords` only once. We've also gotten rid of the mutable `Buffer` (+1 for immutability).
The `for-yield` expression indicates _transformation_: it directly produces a new collection, where each element is transformed from a corresponding element in the original collection. In many other programming languages, this is referred to as "list comprehensions."
One nice thing about `for`-loops and `for`-expressions is that they can contain multiple _generators_, producing a similar effect to nested loops:
val salutations = for (hello <- List("hello", "greetings"); world <- List("world", "interwebs")) yield
"%s %s!".format(hello, world)
This syntax starts to get a bit clunky, the more generators you have, so Scala provides an alternative syntax:
val salutations = for {
hello <- List("hello", "greetings")
world <- List("world", "interwebs")
} yield "%s %s!".format(hello, world)
This is a bit confusing-looking at first, since the braces look like they're delineating a block of statements. It's not difficult to get used to, though, and it's the recommended style when you have multiple generators.
## Assignments and filters
You can also directly assign bindings (equivalent to `val`s) inside the `for { ... }`, not directly generated from elements in a collection:
val salutations = for {
hello <- List("hello", "greetings")
world <- List("world", "interwebs")
salutation = "%s %s!".format(hello, world) // assignment here!
} yield salutation
... which isn't necessarily useful by itself, but becomes very handy when you need to _filter_ some of the results:
val salutations = for {
hello <- List("hello", "greetings")
world <- List("world", "interwebs")
salutation = "%s %s!".format(hello, world)
if salutation.length < 20 // tl;dr
} yield salutation
# Object-oriented constructs
## Classes
scala> class Calculator {
| val brand: String = "HP"
| def add(m: Int, n: Int): Int = m + n
| }
defined class Calculator
scala> val calc = new Calculator
calc: Calculator = Calculator@e75a11
scala> calc.add(1, 2)
res1: Int = 3
scala> calc.brand
res2: String = "HP"
Contained are examples are defining methods with def and fields with val. Methods are just functions that can access the state of the class.
## Constructor
Constructors aren't special methods, they are the code outside of method definitions in your class. Use the `new` keyword to construct an instance.
class Calculator(brand: String) {
val color: String = if (brand == "HP") "black" else "white"
def add(m: Int, n: Int): Int = m + n
}
scala> val calc = new Calculator("HP")
calc: Calculator = Calculator@1e64cc4d
scala> calc.color
res0: String = black
## Expressions
Our BasicCalculator example gave an example of how Scala is expression-oriented. The value color was bound based on an if/else expression. Scala is highly expression-oriented: most things are expressions rather than statements.
scala> val notation = if(calc.color == "black") "reverse polish" else "infix"
notation : java.lang.String = "reverse polish"
## Inheritance
class ScientificCalculator(brand: String) extends Calculator(brand) {
def log(m: Double, base: Double) = {
math.log(m) / math.log(base)
}
}
## Overloading methods
class EvenMoreScientificCalculator(brand: String) extends ScientificCalculator(brand) {
def log(m: Int): Double = log(m, math.exp(1))
}
## Abstract Classes
You can define an `abstract class`, a class that defines some methods but does not implement them. Instead, subclasses that extend the abstract class define these methods. You can't create an instance of an abstract class.
scala> abstract class Shape {
| def getArea():Int // subclass should define this
| }
defined class Shape
scala> class Circle(r: Int) extends Shape {
| def getArea():Int = { r * r * 3 }
| }
defined class Circle
scala> val s = new Shape
<console>:8: error: class Shape is abstract; cannot be instantiated
val s = new Shape
^
scala> val c = new Circle(2)
c: Circle = Circle@65c0035b
## Traits
`trait`s are collections of fields and behaviors that you can extend or mixin to your classes.
trait Car {
val brand: String
}
trait Shiny {
val shineRefraction: Int
def computeShinyness(light: Double) = {
shineRefraction * light
}
}
class BMW extends Car {
val brand = "BMW"
}
One class can extend several traits using the `with` keyword:
class BMW extends Car with Shiny {
val brand = "BMW"
val shineRefraction = 12
}
## Type Parameters
Functions can be generic and work on any type. Here's an example of a generic Cache.
trait Cache[K, V] {
def get(key: K): V
def put(key: K, value: V)
def delete(key: K)
}
Methods can also have type parameters introduced.
def remove[K](key: K)
## apply methods
apply methods give you a nice syntactic sugar for when a class has one main use.
scala> class Bar {
| def apply() = 0
| }
defined class Bar
scala> val bar = new Bar
bar: Bar = Bar@47711479
scala> bar()
res8: Int = 0
## Function Traits
In Scala, functions are represented by a set of traits: `Function0` through `Function22`.
val double = new Function1[Int, Int] {
def apply(x: Int): Int = x * 2
}
is equivalent to
val double = { x: Int => x * 2}
## Objects
Objects are like classes, but with a single instance. Often used for 'static' values.
object Constants {
val Pi = 3.14 // Close enough
}
Classes and Objects can have the same name. The object is called a 'Companion Object'. Here is a trivial example that removes the need to use `new` to create an instance.
class Bar(foo: String)
object Bar {
def apply(foo: String) = new Bar(foo)
}
## Packages
You can organize your code inside of packages.
package com.twitter.example
class ExampleClass
at the top of a file will declare everything in the file to be in that package.
## Imports
In another package, you'd need to `import` that class.
import com.twitter.example.ExampleClass
You can import everything from a package by using the wildcard:
import com.twitter.example._
You can rename symbols when importing into your local scope:
import java.util.{List => UtilList}
## Package Objects
You can create an object for your package. These can be used to delcare anything (traits, classes, functions, etc.) It is often used to declare functions, constants and type definitions, because these cannot be declared out of "thin air": they need to be attached to an object.
package com.twitter
package object example {
val SomeConstant = 42
def theMeaningOfLifeTheUniverseAndEverything() = SomeConstant
}
## Pattern Matching
One of the most useful parts of Scala. Matching on values:
val times = 1
times match {
case 1 => "one"
case 2 => "two"
case _ => "some other number"
}
The `_` in the last case statement is a wildcard; it matches everything. Otherwise, you will suffer a
runtime error if you pass in a number that doesn't match.
## Matching with Guards
times match {
case i if i == 1 => "one"
case i if i == 2 => "two"
case _ => "some other number"
}
Notice how we captured the value in the variable `i`.
## Matching on type
You can use `match` to handle values of different types differently.
def bigger(o: Any): Any = o match {
case i: Int if i < 0 => i - 1
case i: Int => i + 1
case d: Double if d < 0.0 => d - 0.1
case d: Double => d + 0.1
case text: String => text + "s"
}
## Matching on class members
Remember our calculator from earlier? Let's classify them according to type. Here's the painful way first:
def calcType(calc: Calculator) = calc match {
case _ if calc.brand == "hp" && calc.model == "20B" => "financial"
case _ if calc.brand == "hp" && calc.model == "48G" => "scientific"
case _ if calc.brand == "hp" && calc.model == "30B" => "business"
case _ => "unknown"
}
## Case Classes
case classes are used to conveniently store and match on the contents of a class. You can construct them without using new.
scala> case class Calculator(brand: String, model: String)
defined class Calculator
scala> val hp20b = Calculator("hp", "20b")
hp20b: Calculator = Calculator(hp,20b)
case classes automatically have equality and nice toString methods based on the constructor arguments.
scala> val hp20b = Calculator("hp", "20b")
hp20b: Calculator = Calculator(hp,20b)
scala> val hp20B = Calculator("hp", "20b")
hp20B: Calculator = Calculator(hp,20b)
scala> hp20b == hp20B
res6: Boolean = true
case classes can have methods just like normal classes.
## Case Classes with pattern matching
case classes are designed to be used with pattern matching. Let's simplify our calculator classifier example from earlier.
val hp20b = Calculator("hp", "20B")
val hp30b = Calculator("hp", "30B")
def calcType(calc: Calculator) = calc match {
case Calculator("hp", "20B") => "financial"
case Calculator("hp", "48G") => "scientific"
case Calculator("hp", "30B") => "business"
case Calculator(ourBrand, ourModel) => "Calculator: %s %s is of unknown type".format(ourBrand, ourModel)
}
Other alternatives for that last match
case Calculator(_, _) => "Calculator of unknown type"
OR we could simply not specify that it's a Calculator at all.
case _ => "Calculator of unknown type"
OR we could re-bind the matched value with another name
case c@Calculator(_, _) => "Calculator: %s of unknown type".format(c)
## Exceptions
Exceptions are available in Scala via a try-catch-finally syntax that uses pattern matching.
try {
remoteCalculatorService.add(1, 2)
} catch {
case e: ServerIsDownException => log.error(e, "the remote calculator service is unavailable.")
} finally {
remoteCalculatorService.close()
}
## Try-Catch Expressions
`try`s are also expression-oriented
val result: Int = try {
remoteCalculatorService.add(1, 2)
} catch {
case e: ServerIsDownException => {
log.error(e, "the remote calculator service is unavailable.")
0
}
} finally {
remoteCalculatorService.close()
}
Finally will be called after an exception has been handled and is not part of the expression.
## Ordered Collections
`List` and `Vector` are `Seq`uential (ordered) collections.
`List` is a singly-linked list with efficient head access.
scala> val numbers = List(1, 2, 3, 4)
numbers: List[Int] = List(1, 2, 3, 4)
`Vector` is an ordered sequence with efficient random access.
scala> val numbers = Vector(1, 2, 3, 4)
numbers: Vector[Int] = Vector(1, 2, 3, 4)
## `Set`
Sets have no duplicates.
scala> Set(1, 1, 2)
res0: scala.collection.immutable.Set[Int] = Set(1, 2)
scala> res0 contains 5
res1: Boolean = false
## Tuple
A tuple groups together simple logical collections of items without using a class.
scala> val hostPort = ("localhost", 80)
hostPort: (String, Int) = (localhost, 80)
They have accessors that are named by their position (which is 1-based rather than 0-based).
scala> hostPort._1
res0: String = localhost
scala> hostPort._2
res1: Int = 80
Tuples fit with pattern matching nicely.
hostPort match {
case ("localhost", port) => ...
case (host, port) => ...
}
Tuple has some special sauce for making pairs: `->`
scala> 1 -> 2
res0: (Int, Int) = (1,2)
## `Map`
A standard key-value map.
Map("one" -> 1, "two" -> 2)
That looks like special syntax, but it's just a series of pairs.
Map(("one", 1), ("two", 2))
## `Option`
`Option` is a container that may or may not hold something.
The basic interface for Option looks like:
trait Option[T] {
def isDefined: Boolean
def get: T
def getOrElse(t: T): T
}
`Option` itself is generic and has two subclasses: `Some[T]` or `None`
Let's look at an example of how Option is used:
`Map.get` uses `Option` for its return type. Option tells you that the method might not return what you're asking for.
scala> val numbers = Map(1 -> "one", 2 -> "two")
numbers: scala.collection.immutable.Map[Int,String] = Map((1,one), (2,two))
scala> numbers.get(2)
res0: Option[java.lang.String] = Some(two)
scala> numbers.get(3)
res1: Option[java.lang.String] = None
Now our data appears trapped in this `Option`. How do we work with it?
## `Option` (contd)
A first instinct might be to do something conditionally based on the `isDefined` method.
// We want to multiply the number by two, otherwise return 0.
val result = if (res1.isDefined) {
res1.get * 2
} else {
0
}
We would suggest that you use either `getOrElse` or pattern matching to work with this result.
`getOrElse` lets you easily define a default value.
val result = res1.getOrElse(0) * 2
Pattern matching fits naturally with `Option`.
val result = res1 match {
case Some(n) => n * 2
case None => 0
}
## Functional 'Combinators'
`List(1, 2, 3) map squared` applies the function `squared` to the elements of the the list, returning a new list, perhaps `List(1, 4, 9)`. We call operations like `map` combinators. (If you'd like a better definition, you might like [Explanation of combinators](http://stackoverflow.com/questions/7533837/explanation-of-combinators-for-the-working-man) on Stackoverflow.) Their most common use is on the standard data structures.
## `map`
Evaluates a function over each element in the list, returning a list with the same number of elements.
scala> numbers.map((i: Int) => i * 2)
res0: List[Int] = List(2, 4, 6, 8)
or pass in a partially evaluated function
scala> def timesTwo(i: Int): Int = i * 2
timesTwo: (i: Int)Int
scala> numbers.map(timesTwo _)
res0: List[Int] = List(2, 4, 6, 8)
## `foreach`
`foreach` is like map but returns nothing. It is intended for side-effects only.
scala> numbers.foreach((i: Int) => println(i))
You can try to store the return in a value but it'll be of type Unit (i.e. void)
scala> val doubled = numbers.foreach((i: Int) => i * 2)
doubled: Unit = ()
## `filter`
`filter` removes any elements where the function you pass in evaluates to false. Functions that return a Boolean are often called predicate functions.
scala> numbers.filter((i: Int) => i % 2 == 0)
res0: List[Int] = List(2, 4)
scala> def isEven(i: Int): Boolean = i % 2 == 0
isEven: (i: Int)Boolean
scala> numbers.filter(isEven)
res2: List[Int] = List(2, 4)
## `zip`
`zip` aggregates the contents of two lists into a single list of pairs.
scala> List(1, 2, 3).zip(List("a", "b", "c"))
res0: List[(Int, String)] = List((1,a), (2,b), (3,c))
`unzip` does the opposite.
scala> res0.unzip
res1: (List[Int], List[String]) = (List(1, 2, 3),List(a, b, c))
## `partition`
`partition` splits a list based on where it falls with respect to a predicate function.
scala> val numbers = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
scala> val (even, odd) = numbers.partition { _ %2 == 0 }
even: List[Int] = List(2, 4, 6, 8, 10)
odd: List[Int] = List(1, 3, 5, 7, 9))
## `find`
`find` returns the first element of a collection that matches a predicate function (as an `Option`).
scala> numbers.find((i: Int) => i > 5)
res0: Option[Int] = Some(6)
## `drop` & `dropWhile`
`drop` drops the first i elements
scala> numbers.drop(5)
res0: List[Int] = List(6, 7, 8, 9, 10)
`dropWhile` removes the first elements that match a predicate function. For example, if we `dropWhile` odd numbers from our list of numbers, `1` gets dropped (but not `3` which is "shielded" by `2`).
scala> numbers.dropWhile(_ % 2 != 0)
res0: List[Int] = List(2, 3, 4, 5, 6, 7, 8, 9, 10)
## `foldLeft`
scala> numbers.foldLeft(0)((m: Int, n: Int) => m + n)
res0: Int = 55
0 is the starting value (Remember that numbers is a `List[Int]`), and m
acts as an accumulator.
Seen visually:
scala> numbers.foldLeft(0) { (m: Int, n: Int) => println("m: " + m + " n: " + n); m + n }
m: 0 n: 1
m: 1 n: 2
m: 3 n: 3
m: 6 n: 4
m: 10 n: 5
m: 15 n: 6
m: 21 n: 7
m: 28 n: 8
m: 36 n: 9
m: 45 n: 10
res0: Int = 55
## `foldRight`
Is the same as `foldLeft` except it runs in the opposite direction.
scala> numbers.foldRight(0) { (m: Int, n: Int) => println("m: " + m + " n: " + n); m + n }
m: 10 n: 0
m: 9 n: 10
m: 8 n: 19
m: 7 n: 27
m: 6 n: 34
m: 5 n: 40
m: 4 n: 45
m: 3 n: 49
m: 2 n: 52
m: 1 n: 54
res0: Int = 55
## `flatten`
`flatten` collapses one level of nested structure.
scala> List(List(1, 2), List(3, 4)).flatten
res0: List[Int] = List(1, 2, 3, 4)
## `flatMap`
`flatMap` is a frequently used combinator that combines mapping and flattening. `flatMap` takes a function that works on the nested lists and then concatenates the results back together.
scala> val nestedNumbers = List(List(1, 2), List(3, 4))
nestedNumbers: List[List[Int]] = List(List(1, 2), List(3, 4))
scala> nestedNumbers.flatMap(x => x.map(_ * 2))
res0: List[Int] = List(2, 4, 6, 8)
Think of it as short-hand for mapping and then flattening:
scala> nestedNumbers.map((x: List[Int]) => x.map(_ * 2)).flatten
res1: List[Int] = List(2, 4, 6, 8)
## What about `Map`?
Maps can be thought of as a collection of pairs, so all the collection methods work.
scala> val extensions = Map("steve" -> 100, "bob" -> 101, "joe" -> 201)
extensions: scala.collection.immutable.Map[String,Int] = Map((steve,100), (bob,101), (joe,201))
scala> extensions.filter { _._2 < 200 }
res0: scala.collection.immutable.Map[String,Int] = Map((steve,100), (bob,101))
## `andThen`
`andThen` is used to chain the output of one function as the input to another.
val trim = { str: String => str.trim }
val toUpper = { str: String => str.toUpperCase }
val cleanup = trim andThen toUpper
scala> cleanup(" input string ")
res0: String = INPUT STRING
## `PartialFunction`
A Partial Function is a function that's only defined for certain values of the defined type.
`isDefinedAt` is a method on PartialFunction that can be used to determine if the PartialFunction will accept a given argument.
scala> val stars = new PartialFunction[Int, String] {
def apply(n: Int) = "*" * n
def isDefinedAt(n: Int) = n >= 0
}
stars: PartialFunction[Int,String] = <function1>
scala> stars.isDefinedAt(1)
res0: Boolean = true
scala> stars.isDefinedAt(-1)
res1: Boolean = false
## `orElse`
You can define a 'fallback' `PartialFunction` using the `orElse` method.
scala> val wildcard: PartialFunction[Int, String] = { case _ => "something else" }
wildcard: PartialFunction[Int,String] = <function1>
scala> val partial = stars orElse wildcard
partial: PartialFunction[Int,String] = <function1>
scala> partial(5)
res27: String = *****
scala> partial(-1)
res28: String = something else
## The mystery of `case`
A `case` statement is a partial function. It's defined if any of the match statements match.
Since it's just a function, it can be used anywhere a function is used.
scala> case class PhoneExt(name: String, ext: Int)
defined class PhoneExt
scala> val extensions = List(PhoneExt("steve", 100), PhoneExt("robey", 200))
extensions: List[PhoneExt] = List(PhoneExt(steve,100), PhoneExt(robey,200))
scala> extensions.filter { case PhoneExt(name, extension) => extension < 200 }
res0: List[PhoneExt] = List(PhoneExt(steve,100))
## Types in Scala
Scala's powerful type system allows for very rich expression. Some of its chief features are:
* *parametric polymorphism*: roughly, generic programming
* *(local) type inference*: roughly, why you needn't say `val i: Int = 12: Int`
* *existential quantification*: roughly, defining something _for some_ unnamed type
## Scala's Type Hierarchy
* Everything extends from `Any`
* All 'primitives' extend from `AnyVal` (`Int`, `Float`, etc.)
* Everything else extends from `AnyRef` (String, etc.)
* `Nothing` extends from everything (and is the only class that may extend from both `AnyRef` and `AnyVal`)

Credit: http://joelabrahamsson.com/learning-scala-part-eight-scalas-type-hierarchy-and-object-equality/
## Type inference
A traditional objection to static typing is that it has much syntactic overhead. Scala alleviates this by providing _type inference_. We've seen this already:
// Infers that x is of type Int
val x = 128
// Infers that the return type is Int
def plusOne(x: Int) = x + 1
The compiler will teach you which types it can infer and which ones it can't.
## Type Ascriptions
Sometimes you want the compiler to infer some type in particular (eg. for API compatibility). You can use a type 'ascription' for this:
scala> "hello!": AnyRef
res0: AnyRef = hello!
This will fail if the types don't match up:
scala> "hello!": Int
<console>:8: error: type mismatch;
found : java.lang.String("hello!")
required: Int
## Quantification
Sometimes you do not care to be able to name a type variable, for example:
scala> def count[A](l: List[A]) = l.size
count: [A](List[A])Int
Instead you can use "wildcards":
scala> def count(l: List[_]) = l.size
count: (List[_])Int
## Implicits
If you mark a function or value as `implicit`, the compiler can use it to automatically
satisfy some constraints. This is a powerful but tricky type-system feature.
Implicit conversions will let you treat one type as if it were another type.
scala> "Hello World".drop(2)
res0: String = llo World
Implicit parameters let the compiler 'fill in' some of the arguments automatically.
scala> List(1,2,3,4).sum
res0: Int = 10
## Resources
* Scala School: http://twitter.github.io/scala_school/
* Effective Scala: http://twitter.github.io/effectivescala/
* Coursera: https://www.coursera.org/course/progfun
/