1. intro match {
!case PatternMatching()
!!!=> “Welcome”
}
Friday, September 6, 13
2. Who am I?
•Java & Scala developer at Schantz A/S
•Polyglot curious, Coursera junkie
•Interested in HCI and Usability
•https://github.com/JKrag
@jankrag
• Geek, builder and flyer of kites, reptile & cat breeder, Rubik's puzzle fan
Friday, September 6, 13
3. Pattern Matching
• A very powerful feature of Scala
• Java’s “switch” on steroids?
Friday, September 6, 13
4. Java’s switch
• lets you match a ‘value’ agains a number of
cases, and conditionally executes code
• basically only switch on numeric values
• int, byte, etc....
• (Yes, Java 7 has switch on String, but only
syntactic sugar.)
• Performance > nested if’s
Friday, September 6, 13
5. Scala’s ‘match’
• Lets you match a ‘value’ agains complex
patterns
• Can switch on multiple types
• Can match most kind of types by matching
agains the “creation form” of an object
(patience...)
Friday, September 6, 13
6. Scala’s match (cont.)
• Each “case” is a Scala expression, and thus
Each “match” block is an expression
• Can be used as a full function body...
Friday, September 6, 13
7. History
• Pattern matching is nothing new
• Has existed way back in functional
languages
• Most notable early example: ML
• Also found in Haskell, Erlang, OCaml etc.
Friday, September 6, 13
9. Syntax - simple “java like”
def weather(code: Int): String = {
code match {
case 1 => "sun"
case 0 => "rain"
case _ => "error"
}
}
“_” is used as wildcard for the “default” case, when we don’t
need the matched value...
Friday, September 6, 13
10. Syntax
def weather(code: Int) = code match {
case 1 => "sun"
case 0 => "rain"
case _ => "error"
}
match used directly as full function body
Friday, September 6, 13
11. multiple matches
def myPlans(weekday: Int) = weekday match {
! case 1 | 2 | 3 | 4 | 5 => "work"
! case 6 | 7 => "relax"
! case _ => "unknown weekday"
}
Friday, September 6, 13
12. Matching literals
• A literal pattern L matches any value that is
equal (in terms of ==) to the literal L.
Friday, September 6, 13
13. matching strings
def parseArgument(arg: String) = arg match {
case "-h" | "--help" => displayHelp
case "-v" | "--version" => displayVerion
case whatever => unknownArgument(whatever)
}
Friday, September 6, 13
14. Mixed stuff
def handle(msg: Any) = msg match {
! "QUIT" => "recieved stop request"
! 42 => "The answer to the ultimate question"
! _ => "something else"
}
Friday, September 6, 13
15. matching tuples
def moveTo(coord: Any) = coord match {
! case (_, _) => println("received good 2D coord.")
! case (_, _ , _) => println("3D not supported")
! case _ => println("unexpected stuff")
}
Friday, September 6, 13
16. matching on types
• In java, if you need to “detect” types, you
typically use “instance of” and typecast
• In scala, we can match on types, using
variables, and these know the type
Friday, September 6, 13
17. types and variables
def handle(msg: Any) = msg match {
case i:Int => "Int'eresting: " + i
case _:Double => "Doubly so!"
case s:String => "You really want't me to do " + s
}
• introduced variables (typed of course)
• order can be important. Cases checked in
order
Friday, September 6, 13
18. matching tuples -
revisited
def moveTo(coord: Any) = coord match {
! case (a: Int, b: Int) => updateCoordinate(a, b)
! case (_, _ , _) => println("3D not supported")
! case _ => println("unexpected coordinate")
}
Friday, September 6, 13
19. variables
• In general, everything in lowercase is
treated as a “variable”
• constants should start with uppercase
Friday, September 6, 13
20. case matters
class Sample {
! val max = 100
! val MIN = 0
! def process(input: Int) {
! ! input match {
! ! ! case max => println("aaargh.")
! ! ! case MIN => println("matched MIN")
! ! ! case _ => println("unreachable")
! ! }
! }
}
What happens?
Friday, September 6, 13
21. case matters
class Sample {
! val max = 100
! val MIN = 0
! def process(input: Int) {
! ! input match {
! ! ! case max => println("aaargh.")
! ! ! case MIN => println("matched MIN")
! ! ! case _ => println("unreachable")
! ! }
! }
}
What happens?
Compile error
Friday, September 6, 13
22. case matters
class Sample {
! val max = 100
! val MIN = 0
! def process(input: Int) {
! ! input match {
! ! ! case max => println("aaargh.")
! ! ! case MIN => println("matched MIN")
! ! ! case _ => println("unreachable")
! ! }
! }
}
What happens?
Compile error
unreachable code
Friday, September 6, 13
23. case matters
class Sample {
! val max = 100
! val MIN = 0
! def process(input: Int) {
! ! input match {
! ! ! case max => println("aaargh.")
! ! ! case MIN => println("matched MIN")
! ! ! case _ => println("unreachable")
! ! }
! }
}
What happens?
Compile error
unreachable code
fix with: this.max
Friday, September 6, 13
24. Practical example:
Recursive factorial
• Without pattern matching:
def fact(n: Int): Int =
if (n == 0) 1
else n * fact(n - 1)
• With pattern matching:
def fact(n: Int): Int = n match {
case 0 => 1
case n => n * fact(n - 1)
}
• Note: n matches “everything else”
• Note: scope
Friday, September 6, 13
25. Matching List
• List("Apple", "Microsoft")
• List("Scala", "Clojure", "Groovy", _*)
• "array explosion symbol"
Friday, September 6, 13
27. look-alike
list match {
! ! case Nil => "was an empty list"
!! case x :: xs =>
"head was " + x + ", tail was " + xs
}
//remember a list in scala is either Nil, or “something
and a tail”
Friday, September 6, 13
28. look-alike
def length[A](list : List[A]) : Int = list match {
case _ :: tail => 1 + length(tail)
case Nil => 0
}
list match {
! ! case Nil => "was an empty list"
!! case x :: xs =>
"head was " + x + ", tail was " + xs
}
//remember a list in scala is either Nil, or “something
and a tail”
Friday, September 6, 13
30. Guards - example
case msg : Int if (msg < 0) =>
printf("Execution problem. Return code: %d")
case msg : Int if (msg > 0) =>
printf("Succes. Return code: %d")
case _ : Int => printf("Boring")
Friday, September 6, 13
33. Nested - example
object Role extends Enumeration {
! type Role = Value
! val DEV, PM, CEO = Value
}
case class Name(first:String, last:String)
case class Person(name:Name, age:Int, role:Role)
val person = Person(Name("Jan", "Krag"), 42, Role.DEV)
person match {
! case Person(Name(first, last), age:Int, r: Role) =>
first + " " + last + " (aged " + age + ")"
! case _ => "something else"
}
//> res4: java.lang.String = "Jan Krag (aged 42) "
Friday, September 6, 13
34. Pattern binders
• We can assign a binder to part of a pattern
during a match using the @ notation.
• Often usefull when we want a larger part
of a pattern to use on the expression side
• e.g. case pers @ Person(“Dude”, _, _) =>
println(pers)
• binds pers to the whole Person object
Friday, September 6, 13
36. case classes
• Very common use case
• If case class is sealed abstract, compiler can
verify that match is exhaustive
• works because case classes have an auto-
generated unapply method.
Friday, September 6, 13
37. exhaustive match
sealed abstract class Expr
case class Var(name: String) extends Expr
case class Number(num: Double) extends Expr
case class UnOp(operator: String, arg: Expr) extends Expr
case class BinOp(operator: String, left: Expr, right: Expr)
extends Expr
def describe(e: Expr): String = e match {
case Number(_) => "a number"
case Var(_) => "a variable"
}
//warning: match is not exhaustive!
//missing combination UnOp
//missing combination BinOp
Friday, September 6, 13
38. Matching XML fragments
• As Scala has first class support for XML, we
can also match XML fragments:
case <price>{itemPrice}</price> =>
println("price was: " + itemPrice)
Friday, September 6, 13
39. Stable identifiers
def f(x: Int, y: Int) = x match {
case y => ...
}
def f(x: Int, y: Int) = x match {
case `y` => ...
}
Friday, September 6, 13
40. anomymous functions
• case classes can also be used as anonymous
functions, i..e. without a “match” clause:
• { case p1 => b1 ... case pn => bn }
Friday, September 6, 13
41. in Exception handling
try {
throw new j.i.IOException("no such file")
} catch {
case e @ (_ : RuntimeException | _ : j.i.IOException)
=> println(e)
}
// prints out "java.io.IOException: no such file"
Example also demonstrates binding of “alternatives”
expression
Friday, September 6, 13
42. Deconstructing
BillVenners: You said a pattern looks like an expression, but it
seems kind of like a backwards expression. Instead of
plugging values in and getting one result out, you put in one
value, and when it matches, a bunch of values pop back out.
Martin Odersky: Yes. It's really the exact reversal of
construction. I can construct objects with nested
constructors, and maybe I also have some parameters. Let's
say I have a method that takes some parameters and
constructs some complicated object structure from those
parameters. Pattern matching does the reverse. It takes a
complicated object structure and pulls out the parameters
that were used to construct the same structure.
Friday, September 6, 13
43. apply(...) / unapply(...)
• And this is a whole new (albeit important)
subject best left for next time...
Friday, September 6, 13