Showing posts with label matching. Show all posts
Showing posts with label matching. Show all posts

Thursday, February 18, 2010

Chaining Partial Functions with orElse

PartialFunctions are extremely valuable Scala constructs that are used in many APIs. Commonly you will encounter the pattern:
  1. obj match {
  2. case "Val" => // do something
  3. case _ => // handle all other cases
  4. }

It is intuitive and obvious how to share the code of the right hand side if the case statement by factoring that code out to a method. But would it not be useful to be able to factor out an entire case statement (PartialFunction) and later chain them together as needed?

This is indeed possible and very easy to do:
  1. /*
  2. We need to declare Partial Functions so to add brevity I am adding this alias
  3. */
  4. scala> import scala.{PartialFunction => PF}
  5. import scala.{PartialFunction=>PF}
  6. /*
  7. You have to explicitly declare the type because the type inferencer cannot know what type of PartialFunction to create
  8.  
  9. A PartialFunction is Strictly type so some functions can only be used on Ints for example
  10. */
  11. scala> val i : PF[Any, Unit] = {case x:Int => println("int found")}
  12. i: PartialFunction[Any,Unit] = < function1>
  13. scala> val j : PF[Any, Unit] =  {case x:Double => println("Double found")}
  14. j: PartialFunction[Any,Unit] = < function1>
  15. scala> val * : PF[Any, Unit] =  {case x=> println("Something else found")}
  16. *: PartialFunction[Any,Unit] = < function1>
  17. /*
  18. one might think that you can do:
  19. 1 match (i orElse j orElse *)
  20. but in fact (i orElse j orElse *) forms a PartialFunction not a pattern so cannot be used with match.  Instead it must be used as a function
  21. */
  22. scala> (i orElse j orElse *)(1)
  23. int found
  24. scala> (i orElse j orElse *)(1.0)
  25. Double found
  26. scala> (i orElse j orElse *)(true)
  27. Something else found
  28. /*
  29. for specific cases it is possible to chain the an anonymous partial function with the common function
  30. This is not so nice so it is probably best to declare a val instead of inline like this
  31. */
  32. scala> (({case s:String => println("String found")}:PF[Any,Unit]) orElse j orElse *)("hello")
  33. String found

For another example of chaining PartialFunctions the Akka tutorial has a good example in the ChatServer trait: http://jonasboner.com/2010/01/04/introducing-akka.html

Tuesday, February 16, 2010

And Case Statements

Recently I encountered a good question on Stack Overflow about matching.
http://stackoverflow.com/questions/2261358/pattern-matching-with-conjunctions-patterna-and-patternb.

As mentioned in an earlier post Matching with Or case expressions suppose 'or' expression combination using the '|' character. However 'and' combinations are not possible.

One solution is to build an && extractor object as follows:
  1. scala> case object && {  def unapply[A](a: A) = Some((a, a))}
  2. defined module $amp$amp
  3. scala> object StartsWith {  def unapply(s: String) = s.headOption}
  4. defined module StartsWith
  5. scala> object EndsWith {  def unapply(s: String) = s.reverse.headOption}
  6. defined module EndsWith
  7. scala> "foo" match {  case StartsWith('f') && EndsWith('o') => println("f*o") }
  8. f*o

Note: this is a scala 2.7 solution Scala 2.8 can be used to improve the EndsWith extractor by using the method lastOption instead of s.reverse.headOption.

Friday, January 29, 2010

Overcoming Type Erasure in Matching 2 (Variance)

A commenter (Brian Howard) on the post Overcoming Type Erasure in Matching 1 made a very good point:

Is there a way to deal with some type arguments being contravariant? Try the following:

class A

class B extends A

val AAFunction = new Def[Function1[A,A]]

((a:A) => a) match {case AAFunction(f) => Some(f(new A)); case _ => None} // this is OK

((a:A) => new B) match {case AAFunction(f) => Some(f(new A)); case _ => None} // this is OK

((b:B) => b) match {case AAFunction(f) => Some(f(new A)); case _ => None} // gives a ClassCastException, since new A is not a B

There is a way to do this, however the information is not captured in the Manifest. A manifest is not designed to do full reflection it is by design very light and has little impact on performance. So to provide the functionality requested by Brian one needs to add that information to the Extractor Definition. Have have a possible solution below.
  1. scala> class A
  2. defined class A
  3. scala> class B extends A
  4. defined class B
  5. scala> object Variance extends Enumeration {
  6.      |     val Co, Contra, No = Value
  7.      | }
  8. defined module Variance
  9. scala> import Variance._
  10. import Variance._
  11. scala> class Def[C](variance:Variance.Value*)(implicit desired : Manifest[C]) {
  12.      |     def unapply[X](c : X)(implicit m : Manifest[X]) : Option[C] = {
  13.      |         val typeArgsTriplet = desired.typeArguments.zip(m.typeArguments).
  14.      |                                                     zipWithIndex
  15.      |         def sameArgs = typeArgsTriplet forall {
  16.      |             case ((desired,actual),index) if(getVariance(index) == Contra) => 
  17.      |                 desired <:< actual 
  18.      |             case ((desired,actual),index) if(getVariance(index) == No) => 
  19.      |                 desired == actual 
  20.      |             case ((desired,actual),index) => 
  21.      |                 desired >:> actual
  22.      |         }
  23.      |         
  24.      |         val isAssignable = desired.erasure.isAssignableFrom(m.erasure) || (desired >:> m)
  25.      |         if (isAssignable && sameArgs) Some(c.asInstanceOf[C])
  26.      |         else None
  27.      |     }
  28.      |     def getVariance(i:Int) = if(variance.length > i) variance(i) else No
  29.      | }
  30. defined class Def
  31. scala> val AAFunction = new Def[Function1[A,A]]
  32. AAFunction: Def[(A) => A] = Def@64a65760
  33. scala> ((a:A) => a) match {case AAFunction(f) => Some(f(new A)); case _ => None}
  34. res0: Option[A] = Some(A@1bd4f279)
  35. scala> ((a:A) => new B) match {case AAFunction(f) => Some(f(new A)); case _ => None}
  36. res1: Option[A] = None
  37. scala> ((b:B) => b) match {case AAFunction(f) => Some(f(new A)); case _ => None}
  38. res2: Option[A] = None
  39. scala> val BAFunction = new Def[Function1[B,A]](Contra,Co)
  40. BAFunction: Def[(B) => A] = Def@26114629
  41. scala> ((b:A) => b) match {case BAFunction(f) => Some(f(new B)); case _ => None}
  42. res3: Option[A] = Some(B@15dcc3ca)

Thursday, January 28, 2010

Overcoming Type Erasure in matching 1

Since Scala runs on the JVM (it also runs on .NET but we this tip is aimed at Scala on the JVM) much of the type information that is available at compile-time is lost during runtime. This means certain types of matching are not possible. For example it would be nice to be able to do the following:
  1. scala> val x : List[Any] = List(1.0,2.0,3.0)
  2. x: List[Any] = List(1, 2, 3)
  3. scala> x match { case l : List[Boolean] => l(0) }

If you run this code the list matches the list of ints which is incorrect (I have ran the following with -unchecked so it will print the warning about erasure):
  1. scala> val x : List[Any] = List(1.0,2.0,3.0)
  2. x: List[Any] = List(1, 2, 3)
  3. scala> x match { case l : List[Boolean] => l(0) }         
  4. < console>:6: warning: non variable type-argument Boolean in type pattern List[Boolean] is unchecked since it is eliminated by erasure
  5.        x match { case l : List[Boolean] => l(0) }
  6.                           ^
  7. java.lang.ClassCastException: java.lang.Double cannot be cast to java.lang.Boolean
  8. at scala.runtime.BoxesRunTime.unboxToBoolean(Unknown Source)
  9. at .< init>(< console>:6)

Another example is trying to match on structural types. Wouldn't it be nice to be able to do the following (We will solve this in a future post):
  1. scala> "a string" match {
  2.      | case l : {def length : Int} => l.length
  3.      | }
  4. < console>:6: warning: refinement AnyRef{def length: Int} in type pattern AnyRef{def length: Int} is unchecked since it is eliminated by erasure
  5.        case l : {def length : Int} => l.length
  6.                 ^
  7. res5: Int = 8

So lets see what we can do about this. My proposed solution is to create an class that can be used as an extractor when instantiated to do the check.

This is a fairly advanced tip so make sure you have read up on Matchers and Manifests:

The key parts of the next examples are the Def class and the function 'func'. 'func' is defined in the comments in the code block.

The Def class is the definition of what we want to match. Once we have the definition we can use it as an Extractor to match List[Int] instances.

The Def solution is quite generic so it will satisfy may cases where you might want to do matching but erasure is getting in the way.

The secret is to use manifests:
  • When the class is created the manifest for the class we want is captured.
  • Each time a match is attempted the manifest of the class being matched is captured
  • In the unapply method, the two manifests are compared and when they match we can return the matched element but cast to the desired type for the compiler

It is critical to notice the use of the typeArguments of the manifest. This returns a list of the manifests of each typeArgument. You cannot simply compare desired == m because manifest comparisons are not deep. There is a weakness in this code in that it only handles generics that are 1 level deep. For example:

List[Int] can be matched with the following code but List[List[Int]] will match anything that is List[List[_]]. Making the method more generic is an exercise left to the reader.
  1. scala> import reflect._ 
  2. import reflect._
  3. /*
  4. This is the key class
  5. */
  6. scala> class Def[C](implicit desired : Manifest[C]) {
  7.      | def unapply[X](c : X)(implicit m : Manifest[X]) : Option[C] = {
  8.      |   def sameArgs = desired.typeArguments.zip(m.typeArguments).forall {case (desired,actual) => desired >:> actual}
  9.      |   if (desired >:> m && sameArgs) Some(c.asInstanceOf[C])
  10.      |   else None
  11.      | }
  12.      | }
  13. defined class Def
  14. // first define what we want to match
  15. scala> val IntList = new Def[List[Int]]
  16. IntList: Def[List[Int]] = Def@6997f7f4
  17. /*
  18. Now use the object as an extractor.  the variable l will be a typesafe List[Int]
  19. */
  20. scala> List(1,2,3) match { case IntList(l) => l(1) : Int ; case _ => -1 }
  21. res36: Int = 2
  22. scala> List(1.0,2,3) match { case IntList(l) => l(1) : Int ; case _ => -1 }
  23. res37: Int = -1
  24. // 32 is not a list so it will fail to match
  25. scala> 32 match { case IntList(l) => l(1) : Int ; case _ => -1 }
  26. res2: Int = -1
  27. scala> Map(1 -> 3) match { case IntList(l) => l(1) : Int ; case _ => -1 }
  28. res3: Int = -1
  29. /*
  30. The Def class can be used with any Generic type it is not restricted to collections
  31. */
  32. scala> val IntIntFunction = new Def[Function1[Int,Int]]
  33. IntIntFunction: Def[(Int) => Int] = Def@5675e2b4
  34. // this will match because it is a function
  35. scala> ((i:Int) => 10) match { case IntIntFunction(f) => f(3); case _ => -1}
  36. res38: Int = 10
  37. // no match because 32 is not a function
  38. scala> 32 match { case IntIntFunction(f) => f(3); case _ => -1}
  39. res39: Int = -1
  40. // Cool Map is a function so it will match
  41. scala> Map(3 -> 100) match { case IntIntFunction(f) => f(3); case _ => -1}
  42. res6: Int = 100
  43. /*
  44. Now we see both the power and the limitations of this solution.
  45. One might expect that:
  46.    def func(a:Any) = {...} 
  47. would work.  
  48. However, if the function is defined with 'Any' the compiler does not pass in the required information so the manifest that will be created will be a Any manifest object.  Because of this the more convoluted method declaration must be used so the type information is passed in. 
  49. I will discuss implicit parameters in some other post
  50. */
  51. scala> def func[A](a:A)(implicit m:Manifest[A]) = {
  52.      |   a match {
  53.      |     case IntList(l) => l.head                   
  54.      |     case IntIntFunction(f) => f(32)             
  55.      |     case i:Int => i                             
  56.      |     case _ => -1                                
  57.      |   } 
  58.      | }
  59. func: [A](a: A)(implicit m: Manifest[A])Int
  60. scala> func(List(1,2,3))                           
  61. res16: Int = 1
  62. scala> func('i')                                   
  63. res17: Int = -1
  64. scala> func(4)
  65. res18: Int = 4
  66. scala> func((i:Int) => i+2)
  67. res19: Int = 34
  68. scala> func(Map(32 -> 2))
  69. res20: Int = 2

Tuesday, January 26, 2010

Guard Sugar

This topic is a simple tip for a cleaner syntax for guards. Guards/filters are statements in for-comprehensions and case statements that guard or filter matches. Often you will see them as follows:
  1. scala> for (i <- 1 to 10; if (i % 2 == 1)) yield i
  2. res0: scala.collection.immutable.IndexedSeq[Int] = IndexedSeq(1, 3, 5, 7, 9)
  3. scala> util.Random.nextInt(10) match {
  4.      | case i if(i>5) => println("hi")
  5.      | case _ => println("low")
  6.      | }
  7. low

However you have the option to apply a little sugar to the statements cleaning them up a little:
  1. scala> for (i <- 1 to 10; if i % 2 == 1) yield i  
  2. res2: scala.collection.immutable.IndexedSeq[Int] = IndexedSeq(1, 3, 5, 7, 9)
  3. scala> util.Random.nextInt(10) match {          
  4.      | case i if i>5 => println("hi")           
  5.      | case _ => println("low")                 
  6.      | }
  7. hi

As you can see the brackets are optional. That is because in both cases the brackets are not required for the parser to determine the start and end of the guard statements. They are added so that the "normal" syntax used in the typical if statements will compile.
  1. scala> 10 match { case i if i == 1 || i == 10 => "obviously this is a match"
  2. res4: java.lang.String = obviously this is a match
  3. /*
  4. In case statements you can split the if almost any way you want because it is very clearly bound by the if and the => that is required for all case statements with a guard
  5. */
  6. scala> 10 match { case i if i == 1 ||                                        
  7.      | i == 10 => "obviously this is a match"}
  8. res5: java.lang.String = obviously this is a match
  9. scala> 10 match {        
  10.      | case i
  11.      | if
  12.      | i == 1
  13.      | ||
  14.      | i == 10 
  15.      | => "matched"
  16.      | }
  17. res6: java.lang.String = matched
  18. /*
  19. For-comprehensions are not as flexible since it is harder for the compiler to determine where the guard ends. So try to keep it on one line or otherwise make it a function.  That is probably good advice for case statements as well.
  20. */
  21. scala> for { 
  22.      | x <- 1 to 10
  23.      | if
  24.      | x > 10
  25.      | || x == 2 } yield x
  26. < console>:5: error: '<-' expected but integer literal found.
  27.        || x == 2 } yield x
  28.                ^
  29. < console>:5: error: illegal start of simple expression
  30.        || x == 2 } yield x
  31.                  ^
  32. scala> for {              
  33.      | x <- 1 to 10       
  34.      | if x == 1 || x == 10 } yield x
  35. res7: scala.collection.immutable.IndexedSeq[Int] = IndexedSeq(1, 10)

Monday, January 25, 2010

Matching with Or

There is a common case where several expressions in a match statement need to have the same result. For example:

  1. scala> "word" match {
  2.      | case a @ "word" => println(a)
  3.      | case a @ "hi" => println(a)
  4.      | }
  5. word

this could be changed to:
  1. scala> "word" match {               
  2.      | case a if(a == "word" || a == "hi") => println(a)
  3.      | }
  4. word

but this is rather verbose. Another option is to use or matching:
  1. scala> "word" match { case a @ ("word" | "hi") => println(a)}
  2. word


  1. /*
  2. it is possible to provide alternatives in matching
  3. Anything more advanced needs to be handled with a guard
  4. See the last examples
  5. */
  6. scala> 1 match { case _:Int | _:Double => println("Found it")}
  7. Found it
  8. // v will be the value if v is either an Int or a Double
  9. scala> 1.0 match { case v @ ( _:Int | _:Double) => println(v)}
  10. 1.0
  11. // Having variables as part of the patterns is not permitted
  12. scala> 1.0 match { case v:Int | d:Double => println(v)} 
  13. < console>:5: error: illegal variable in pattern alternative
  14.        1.0 match { case v:Int | d:Double => println(v)}
  15.                         ^
  16. < console>:5: error: illegal variable in pattern alternative
  17.        1.0 match { case v:Int | d:Double => println(v)}
  18.                                                     ^
  19. /*
  20. Variables are not permitted not even when the name is the same.
  21. */
  22. scala> 1.0 match { case d:Int | d:Double => println(d)}
  23. < console>:5: error: illegal variable in pattern alternative
  24.        1.0 match { case d:Int | d:Double => println(d)}
  25.                         ^
  26. < console>:5: error: illegal variable in pattern alternative
  27.        1.0 match { case d:Int | d:Double => println(d)}
  28.                                 ^

Monday, January 11, 2010

Matching Nulls

As a bit of explanation of one of the techniques in Regex Matching this topic reviews matching nulls.

  1. // No surprise _ matches everything
  2. scala> null match { case _ => println("null") }
  3. null
  4. // again null matches null
  5. scala> null match { case null => println("null") }
  6. null
  7. // a is bound to anything including null
  8. scala> null match { case a => println("matched value is: "+a) }
  9. matched value is: null
  10. scala> val a:String = null
  11. a: String = null
  12. // basically same as last example
  13. scala> a match {case a => println( a + " is null")}          
  14. null is null
  15. // Any matches any non-null object
  16. scala> null match {                                                
  17.      | case a:Any => println("matched value is: "+a)               
  18.      | case _ => println("null is not Any")
  19.      | }
  20. null is not Any
  21. scala> val d:String = null                             
  22. d: String = null
  23. // In fact when matching null does not match any type
  24. scala> d match {                                       
  25.      | case a:String => println("matched value is: "+a)
  26.      | case _ => println("no match")                   
  27.      | }
  28. no match
  29. scala> val data:(String,String) = ("s",null)         
  30. data: (StringString) = (s,null)
  31. // matching can safely deal with nulls but don't forget the catch all
  32. // clause or you will get a MatchError
  33. scala> data match {                                  
  34.      | case (a:String, b:String) => "shouldn't match"
  35.      | case (a:String, _) => "should match"          
  36.      | }
  37. res10: java.lang.String = should match
  38. // again null is all objects but will not match Any
  39. scala> data match {                            
  40.      | case (a:String, b:Any) => "shouldn't match"   
  41.      | case (a:String, _) => "should match"       
  42.      | }
  43. res12: java.lang.String = should match

Regular Expression 3: Regex matching

This post covers basically the same things as Matching Regular Expressions but goes into a bit more detail. I recommend reading both posts since there is unique information in each.

The primary new item I show here is that more advanced matching techniques can be used but more importantly all groups are matched even groups that are within another group.

Note: The examples use Scala 2.8. Most examples will work with 2.7 but I believe the last example is Scala 2.8 only.

  1. scala> val date = "11/01/2010"
  2. date: java.lang.String = 11/01/2010
  3. scala> val Date = """(\d\d)/(\d\d)/(\d\d\d\d)""".r
  4. Date: scala.util.matching.Regex = (\d\d)/(\d\d)/(\d\d\d\d)
  5. /*
  6. When a Regex object is used in matching each group is assigned to a variable
  7. */
  8. scala> val Date(day, month, year) = date          
  9. day: String = 11
  10. month: String = 01
  11. year: String = 2010
  12. scala> val Date = """(\d\d)/((\d\d)/(\d\d\d\d))""".r
  13. Date: scala.util.matching.Regex = (\d\d)/((\d\d)/(\d\d\d\d))
  14. /*
  15. This example demonstates how all groups must be assigned, if not there will be a matchError thrown
  16. */
  17. scala> val Date(day, monthYear, month, year) = date
  18. day: String = 11
  19. monthYear: String = 01/2010
  20. month: String = 01
  21. year: String = 2010
  22. scala> val Date(day, month, year) = date           
  23. scala.MatchError: 11/01/2010
  24. at .< init>(< console>:5)
  25. at .< clinit>(< console>)
  26. // but placeholders work in Regex matching as well:
  27. scala> val Date(day, _, month, year) = date
  28. day: String = 11
  29. month: String = 01
  30. year: String = 2010
  31. scala> val Names = """(\S+) (\S*)""".r             
  32. Names: scala.util.matching.Regex = (\S+) (\S*)
  33. scala> val Names(first, second) = "Jesse Eichar"
  34. first: String = Jesse
  35. second: String = Eichar
  36. /*
  37. If you want to use Regex's in assignment you must be sure the match will work.  Otherwise you should do real matching
  38. */
  39. scala> val Names(first, second) = "Jesse"       
  40. scala.MatchError: Jesse
  41. at .< init>(< console>:5)
  42. at .< clinit>(< console>)
  43. scala> val M = """\d{3}""".r
  44. M: scala.util.matching.Regex = \d{3}
  45. /*
  46. There must be a group in the Regex or match will fail
  47. */
  48. scala> val M(m) = "Jan"
  49. scala.MatchError: Jan
  50. at .< init>(< console>:5)
  51. at .< clinit>(< console>)

The following are a few more complex examples
  1. scala> val Date = """((\d\d)/(\d\d)/(\d{4}))|((\w{3}) (\d\d),\s?(\d{4}))""".r         
  2. Date: scala.util.matching.Regex = ((\d\d)/(\d\d)/(\d{4}))|((\w{3}) (\d\d),\s?(\d{4}))
  3. /*
  4. The Regex has an or in it.  So only 1/2 of the groups will be non-null.
  5. If the first group is a String then it is non-null and the next three elements
  6. the pattern will be day/month/year
  7. Otherwise if the 5th group is a String then the patter will be month day, year
  8. Lastly a catch all
  9. */
  10. scala> def printDate(date:String) = date match {                                      
  11.      | case Date(_:String,day,month,year,_,_,_,_) => (day,month,year)                 
  12.      | case Date(_,_,_,_,_:String,month,day,year) => (day,month,year) // process month
  13.      | case _ => ("x","x","x")                                                        
  14.      | }
  15. printDate: (date: String)(StringStringString)
  16. scala> printDate("Jan 01,2010"
  17. res0: (StringStringString) = (01,Jan,2010)
  18. scala> printDate("01/01/2010"
  19. res1: (StringStringString) = (01,01,2010)
  20. /*
  21. A silly example which drops the first element of the date string
  22. not useful but this demonstrates that we are matching agains a sequence so 
  23. the _* can be used to match the rest of the groups
  24. */
  25. scala> def split(date:String) = date match {         
  26.      | case d @ Date(_:String ,_*) => d drop 3       
  27.      | case d @ Date(_,_,_,_,_:String,_*) => d drop 4
  28.      | case _ => "boom"                              
  29.      | }
  30. split: (date: String)String
  31. scala> split ("Jan 31,2004")
  32. res5: String = 31,2004
  33. scala> split ("11/12/2004"
  34. res6: String = 12/2004
  35. /*
  36. This is just a reminder that the findAllIn returns an iterator which (since it is probably a short iterator) can be converted to a sequence and processed with matching
  37. */
  38. scala> val Seq(one,two,_*) = ("""\d\d/""".r findAllIn "11/01/2010" ).toSeq  
  39. one: String = 11/
  40. two: String = 01/
  41. scala> val Seq(one,two) = ("""\d\d/""".r findAllIn "11/01/2010" ).toSeq   
  42. one: String = 11/
  43. two: String = 01/
  44. // drop the two first matches and assign the rest to d
  45. scala> val Seq(_,_,d @ _*) = ("""\d\d/""".r findAllIn "11/01/20/10/" ).toSeq
  46. d: Seq[String] = ArrayBuffer(20/, 10/)

Wednesday, January 6, 2010

Matching in for-comprehensions

At a glance a for-comprehension appears to be equivalent to a Java for-loop, but it is much much more than that. As shown in post: for-comprehensions, for-comprehensions can have guards which filter out which elements are processed:
  1. scala> for ( x <- 1 to 10; if (x >4) ) println(x)
  2. 5
  3. 6
  4. 7
  5. 8
  6. 9
  7. 10

They can be used to construct new collections:
  1. scala>?for(?i?<-?List(?"a",?"b",?"c")?)?yield?"Word:?"+i
  2. res1:?List[java.lang.String]?=?List(Word:?a,?Word:?b,?Word:?c)

They can contain multiple generators:
  1. scala> for {x <- 1 to 10                           
  2.      |      if(x%2 == 0)
  3.      |      y <- 1 to 5} yield (x,y)
  4. res1: scala.collection.immutable.IndexedSeq[(Int, Int)] = IndexedSeq((2,1), (2,2), (2,3), (2,4), (2,5), (4,1), (4,2), (4,3), (4,4), (4,5), (6,1), (6,2), (6,3), (6,4), (6,5), (8,1), (8,2), (8,3), (8,4), (8,5), (10,1), (10,2), (10,3), (10,4), (10,5))

What has not been covered is that the assignments also does pattern matching:
  1. scala> for ( (x,y) <- (6 to 1 by -2).zipWithIndex) println (x,y) 
  2. (6,0)
  3. (4,1)
  4. (2,2)

This is not surprising as this also occurs during normal assignment. But what is interesting is that the pattern matching can act as a guard as well. See Extractor examples and Assignment and Parameter Objects for more information of pattern matching and extractors.
  1. scala> val args = Array( "h=2""b=3")
  2. args: Array[java.lang.String] = Array(h=2, b=3)
  3. scala> val Property = """(.+)=(.+)""".r 
  4. Property: scala.util.matching.Regex = (.+)=(.+)
  5. scala> for {Property(key,value) <- args } yield (key,value)
  6. res0: Array[(String, String)] = Array((h,2), (b,3))
  7. scala> Map(res0:_*)
  8. res1: scala.collection.immutable.Map[String,String] = Map(h -> 2, b -> 3)
  9. scala> res1("h")
  10. res3: String = 2

Now just for fun here is a similar example but using symbols instead of strings for the key values:
  1. scala> val args = Array( "h=2""b=3")
  2. args: Array[java.lang.String] = Array(h=2, b=3)
  3. scala> val Property = """(.+)=(.+)""".r 
  4. Property: scala.util.matching.Regex = (.+)=(.+)
  5. scala> for {Property(key,value) <- args } yield (Symbol(key),value)
  6. res0: Array[(Symbol, String)] = Array(('h,2), ('b,3))
  7. scala> Map(res0:_*)
  8. res1: scala.collection.immutable.Map[Symbol,String] = Map('h -> 2, 'b -> 3)
  9. scala> res1('h)
  10. res2: String = 2

Monday, November 30, 2009

Overloaded Unapply

This topic is related to previous posts on matching. I recommend reading some of them as well:
Since an Extactor is just an object with an unapply method it logically follows that an Extractor can have an overloaded unapply method. In other words can have an unapply(String) and an unapply(Int); allowing matching Strings and Ints.
  1. scala> object T{                                                         
  2.      |  def unapply(v:String)= if(v== "s") Some("yay"else None
  3.      |  def unapply(v:Int) = if(v==1) Some("hmmm"else None
  4.      | }
  5. defined module T
  6. scala> 1 match { case T(x) => println(x) }
  7. hmmm
  8. scala> "s" match { case T(x) => println(x) }
  9. yay
  10. scala> object T{                                 
  11.      | def unapplySeq(v:String) = if (v=="x") Some(List(1,2,3)) else None
  12.      | def unapplySeq(v:Int) = if (v==1) Some(List("one","two")) else None
  13.      | }
  14. defined module T
  15. scala>  "x"  match { case T(x,y,z) => println(x,y,z) }
  16. (1,2,3)
  17. scala>  1  match { case T(x,y) => println(x,y) }  
  18. (one,two)

Friday, November 20, 2009

Either

The Scala Either class is similar in function as Option but it represents a choice. For example a method can return Either an Int or an Exception. Usage example:
  1. // If string can be converted to an Int then return Right[Int] otherwise
  2. // return a Left[String] with the error message
  3. // by convention the error state will always be the Left
  4. scala> def toInt(string:String):Either[String,Int] = {
  5.      | try { Right(string.toInt) }
  6.      | catch { case e => Left("Error: "+e.getMessage) }
  7.      | }
  8. toInt: (string: String)Either[String,Int]
  9. scala> toInt("1")
  10. res0: Either[String,Int] = Right(1)
  11. scala> toInt("booger")
  12. res1: Either[String,Int] = Left(Error: For input string: "booger")

By convention if one return value is a error state and the other the expected state then Right will contain the expected state and Left will contain the error state.
  1. scala> def toInt(string:String):Either[String,Int] = {
  2.      | try { Right(string.toInt) }
  3.      | catch { case e => Left("Error: "+e.getMessage) }
  4.      | }
  5. toInt: (string: String)Either[String,Int]
  6. scala> toInt("1")
  7. res0: Either[String,Int] = Right(1)
  8. scala> toInt("booger")
  9. res1: Either[String,Int] = Left(Error: For input string: "booger")
  10. // you can test if the value is a left or right value quite easily
  11. scala> res0.isLeft
  12. res2: Boolean = false
  13. scala> res1.isRight
  14. res3: Boolean = false
  15. scala> res0.isRight
  16. res4: Boolean = true
  17. scala> res1.isLeft 
  18. res5: Boolean = true
  19. // or matching can be used
  20. scala> res0 match {                                   
  21.      | case Left(l) => println("it is a left")        
  22.      | case Right(r) => println("it is a right")      
  23.      | }
  24. it is a right
  25. scala> res1 match {                             
  26.      | case Left(l) => println("it is a left")  
  27.      | case Right(r) => println("it is a right")
  28.      | }
  29. it is a left
  30. // Perhaps cleaner than matching even is being able to pass 
  31. // functions to the fold method:
  32. // define one function for each side of the either
  33. // first the function to handle the left side case
  34. scala> def leftFunc(errMsg:String) = println("there has been an error")             
  35. leftFunc: (errMsg: String)Unit
  36. // next the function the handle the right side case
  37. scala> def rightFunc(i:Int) = println(i+" was calculated")              
  38. rightFunc: (i: Int)Unit
  39. // then you can pass the two functions to the fold method
  40. // and the correct one will be invoked
  41. scala> res0 fold (leftFunc _, rightFunc _)
  42. 1 was calculated
  43. scala> res1 fold (leftFunc _, rightFunc _)
  44. there has been an error

Thursday, October 29, 2009

Boolean Extractors

As discussed in other topics there are several ways to create custom extractors for objects. See
There is one more custom extractor that can be defined. Simple boolean extractors. They are used as follows:
  1. scala>"hello world"match {                   
  2.      | case HasVowels() => println("Vowel found")
  3.      | case _ => println("No Vowels")            
  4.      | }
  5. Vowel found

A boolean extractor is an object that returns Boolean from the unapply method rather than Option[_].

Examples:
  1. scala>object HasVowels{ defunapply(in:String):Boolean = in.exists( "aeiou" contains _ ) }
  2. defined module HasVowels
  3. // Note that HasVowels() has ().
  4. // This is critical because otherwise the match checks whether
  5. // the input is the HasVowels object.
  6. // The () forces the unapply method to be used for matching
  7. scala>"hello world"match {
  8.      | case HasVowels() => println("Vowel found")
  9.      | case _ => println("No Vowels")
  10.      | }
  11. Vowel found
  12. // Don't forget the ()
  13. scala>"kkkkkk"match {    
  14.      | case HasVowels() => println("Vowel found")
  15.      | case _ => println("No Vowels")
  16.      | }
  17. No Vowels
  18. scala>class HasChar(c:Char) {           
  19.      | defunapply(in:String) = in.contains(c)
  20.      | }
  21. defined class HasChar
  22. scala>val HasC = new HasChar('c')
  23. HasC: HasChar = HasChar@3f2f529b
  24. // Don't forget the () it is required here as well
  25. scala>"It actually works!"match {
  26.      | case HasC() => println("a c was found")
  27.      | case _ => println("no c found")  
  28.      | }
  29. a c was found
  30. // Don't forget the ()
  31. scala>"hello world"match { 
  32.      | case HasC() => println("a c was found")
  33.      | case _ => println("no c found")       
  34.      | }
  35. no c found