Last active
December 14, 2015 09:50
-
-
Save sam/5067898 to your computer and use it in GitHub Desktop.
Pattern Matching examples. Try them out in your REPL!
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// One way to think about "match" is as a "map" method for arbitrary objects: | |
// So I can "map" a number: | |
10 match { case i => i * 4 } | |
// Mapping Tuples is especially awesome: | |
(1, "one") match { case (i,s) => i + 1 -> s"$s!!!" } | |
// Ok, maybe you're not all that impressed. Now that you know | |
// you can "map" a Tuple with Pattern Matching, it makes using | |
// Tuples as return values much more powerful though: | |
def squareAndSum(values:List[Int]):(List[Int], Int) = { | |
values.foldLeft((List[Int](), 0)) { case ((l,sum), i) => | |
(l ++ List(i * i), sum + i) | |
} | |
} | |
// And not a temporary variable in sight: | |
squareAndSum(List(1,2,3,4)) match { case (powers, sum) => | |
println(s"Sum: $sum\nPowers:") | |
powers.foreach(println) | |
} | |
// This is especially useful with recursive methods like a foldLeft operation | |
// that needs to return both a list plus an accumulator. Sort of a "foldMap". | |
// Case classes have "Extractors", which allows us to refer | |
// to their inner fields in pattern matching: | |
case class Account(balance:Int) | |
// And now we can "map" an Account using another account as input without | |
// having to use a temporary variable for our matched Account: | |
Account(1000) match { case Account(balance) => Account(balance + 100) } | |
// Here's how you might achieve the above without pattern-matching: | |
val bob = Account(1000) | |
Account(bob.balance + 100) | |
// Or if there were other fields you needed to preserve as well: | |
case class Account(balance:Int, term:Int) | |
val bob = Account(1000, 36) | |
bob.copy(balance = bob.balance + 100) | |
// And the same for pattern matching: | |
Account(1000, 36) match { case a:Account => a.copy(a.balance + 100) } | |
// Or: | |
Account(1000, 48) match { case Account(b,t) => Account(b + 100, t) } | |
// Note because of the very narrow scope, I don't think there's much of | |
// an argument for "descriptive variable names". Everyone knows what (a,i) | |
// is when reducing a List[Int]. Everyone knows what T, K or M[_] is when | |
// dealing with generics. Everyone knows what (i) means in a mapping | |
// operation. There's conventions here when dealing with a narrow scope. | |
// It's a one liner. It's obvious. If it gets bigger, fix it. | |
// Do what feels right. The important thing is that you're not leaking | |
// this narrow scope so someone else has to guess what Account.b is. | |
// The context is what sets the tone here. What's good advice in the | |
// context of your larger program (descriptive variable names) is just | |
// tedious boiler-plate in your one-liner case expression. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment