intro to pattern matching in scala
DESCRIPTION
Slide deck used for a presentation i gave at CPH Scala Group meeting, Oct. 5th, 2013 in Copenhagen.TRANSCRIPT
intro match {!case PatternMatching()!!!=> “Welcome”}
Friday, September 6, 13
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
Pattern Matching
• A very powerful feature of Scala
• Java’s “switch” on steroids?
Friday, September 6, 13
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
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
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
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
Lets get on with it...
Friday, September 6, 13
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
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
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
Matching literals
• A literal pattern L matches any value that is equal (in terms of ==) to the literal L.
Friday, September 6, 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
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
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
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
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
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
variables
• In general, everything in lowercase is treated as a “variable”
• constants should start with uppercase
Friday, September 6, 13
case mattersclass 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
case mattersclass 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?Com
pile err
or
Friday, September 6, 13
case mattersclass 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?Com
pile err
or
unreachable code
Friday, September 6, 13
case mattersclass 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?Com
pile err
or
unreachable codefix with: this.max
Friday, September 6, 13
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: scopeFriday, September 6, 13
Matching List
• List("Apple", "Microsoft")
• List("Scala", "Clojure", "Groovy", _*)
• "array explosion symbol"
Friday, September 6, 13
look-alike
Friday, September 6, 13
look-alikelist 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
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
Guards
• Powerful addition.
• Allows for “conditional” matching
Friday, September 6, 13
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
More advanced topics?Quick breeze through
Friday, September 6, 13
Nested patterns
• case List(Cat(name), Owner(first, last))
Friday, September 6, 13
Nested - exampleobject 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
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
Regular expressions
val Name = """(\w+)\s+(\w+)""".r"Jan Krag" match { case Name(first,last) => println("found: ", first, last) case _ => println("oh no!")}
(found: ,Jan,Krag)
Friday, September 6, 13
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
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
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
Stable identifiersdef f(x: Int, y: Int) = x match {
case y => ...}
def f(x: Int, y: Int) = x match { case `y` => ...}
Friday, September 6, 13
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
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
Deconstructing Bill Venners: 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
apply(...) / unapply(...)
• And this is a whole new (albeit important) subject best left for next time...
Friday, September 6, 13