2. I Believe You've Met Mr. “switch”...
switch(foo) {
case 1 : doBar("two"); break;
case 2 : doBar("one"); break;
default: doBar("forty-two");
}
I can feel the excitement!
2
4. Scala's “match” Like “switch”
var foo : Int = 5
foo match {
case 1 => doBar("two")
case 2 =>
{ doBar("one"); doBar("two") }
case _ => doBar("lemon curry?")
}
No “break”. Each clause is self-contained
Matches are tried in order, first match wins
What in the heck is this “_” nonsense?
4
5. Match Against Broader Range of Types
def literalMatch (in: Any) {
in match {
case 1 => doBar("One")
case "test" => doBar("test")
case 'x' => doBar("x")
case 2.2f => doBar("float")
case _ => doBar("lemon curry?")
}
}
You can think of this like nested “if” statements
5
6. Alternate Patterns
def literalMatch (in: Any) {
in match {
case 1 | 2 | 3 => doBar("One to three")
case "this" | "that" => doBar("the other")
case _ => doBar("lemon curry?")
}
}
“|” allows clause to match multiple values
6
7. Binding Variables in Matches
def literalMatch (in: Any) {
in match {
case n @ (1 | 2 | 3) => doBar("1-3:" + n)
case t @ ("this" | "that") =>
DoBar(t + " and the other")
case x => doBar("We defaulted on " + x)
}
}
You can bind a complex pattern with “x @ pattern”
Just a variable as a pattern matches anything
7
8. Matching on Type
def typeMatch (in: Any) {
in match {
case i : Int => doBar("Int : " + i)
case s : String => doBar(s)
case _ => // NOOP
}
}
8
9. Matching on Generic Types
def typeMatch (in: Any) {
in match {
case ls : List[String] => doBar("danger!")
case li : List[Int] => doBar("never happens")
case _ => // NOOP
}
}
You can't match on generic types because of erasure
The compiler will warn about unchecked conversions if you
do this
9
11. <Ahem> Guards Permit Fine-Grained Selection
def fifenator (in: Any) {
in match {
case i : Int if i > 12 && i < 47 => doBar("Int : " + i)
case s : String if s.startsWith("DOSUG") => doBar(s)
case _ => // NOOP
}
}
A guard is just a conditional clause for the match
Because the type is being matched on the left, you have
direct access to the type methods and fields without casts
11
12. A Brief Digression into Case Classes
case class Character(show : String, name : String)
A Case Class is a special type of class that automatically
adds methods for equals, hashcode, toString, and
PATTERN MATCHING
12
13. A Brief Digression into Case Classes
def tvTime (c : Character) = c match {
case Character(title, "Fred") =>
doBar("Fred from " + title)
case Character("Flintstones", n) => doBar(n)
case c @ Character(_,_) => doBar(c.name)
}
Constructor arguments automatically become properties that
you can match against
13
14. But Wait, There's More!
Custom Extractors
Sealed class hierarchy
enforcement
Cleans Tough Stains!
Gentle on Hands!
Repels Cougars!
And more!
14
15. Sealed Hierarchy Enforcement
sealed abstract class EntityType(val name : String)
case class Animal(n : String) extends EntityType(n)
case class Vegetable(n : String) extends EntityType(n)
case class Mineral(n : String) extends EntityType(n)
“sealed” indicates that all direct subclasses are defined in
the same source file
Provides the compiler with a guaranteed bound on the type
hierarchy:
scala> def questionOne(t : EntityType) = t match {
| case Animal(name) => println("Animal: " + name)
| case Vegetable(name) => println("Veggie: " + name)
|}
<console>:8: warning: match is not exhaustive!
missing combination Mineral
def questionOne(t : EntityType) = t match { 15
16. Custom Extraction Basics
object Something {
def unapply(input : Foo) : Option[Bar] = {
if (input.bar)
Some(input.toBar)
else
None
}
}
An “object” is a singleton in the VM, defined as you would a
class
Methods on an object equivalent to “static” methods in Java
16
17. Custom Extraction Basics
object Something {
def unapply(input : Foo) : Option[Bar] = {
if (input.bar)
Some(input.toBar)
else
None
}
}
Option is a predefined Scala type that has only two
subclasses: Some and None
A Some holds a typed value
17
18. Custom Extraction Basics
object Something {
def unapply(input : Foo) : Option[Bar] = {
if (input.bar)
Some(input.toBar)
else
None
}
}
The “unapply” method holds special significance to the
compiler
Is used to attempt an extraction/match from a given input
Returning Some(something) indicates a match
Returning None indicates no match
18
19. Custom Extraction: IP Address
object Octet {
def unapply(input : String) = try {
val octet = input.toInt
if (octet >= 0 && octet < 256)
Some(octet)
else
None
} catch {
case _ => None
}
}
First we just define what an octet in an IP is:
An Int...
Between 0 and 255
19
20. Custom Extraction: IP Address
object IP {
def unapplySeq(input : String) : Option[Seq[Int]] =
input.split('.') match {
case Array(Octet(a), Octet(b), Octet(c), Octet(d)) =>
Some(List(a,b,c,d))
case _ => None
}
}
“unapplySeq” allows us to return a sequence of results on a
match
We nest our Octet matcher to match each IP component
20
21. Custom Extraction in Action
scala> def sumIP (address : String) = address match {
| case IP(7, b, c, d) => println("Jackpot!"); Some(7 + b + c + d)
| case IP(a,b,c,d) ⇒ Some(a + b + c + d)
| case _ => None
|}
sumIP: (address: String)Option[Int]
scala> sumIP("12.25.233.61")
res5: Option[Int] = Some(331)
scala> sumIP("12.25.233")
res6: Option[Int] = None
scala> sumIP("7.25.233.61")
Jackpot!
res7: Option[Int] = Some(326)
21