Monday, September 28, 2009

Extractors 1 (Unapply)

When defining a match such as case Tuple2(one, two) the methods Tuple2.unapply and Tuple2.unapplySeq are called to see if that case can match the input. If one of methods return a Some(...) object then the case is considered to be a match. These methods are called Extractor methods because they essentially decompose the object into several parameters.

I will cover unapplySeq later.

Examples are the best way to illustrate the issue:
  1. // The unapply method of this object takes a string and returns an Option[String]
  2. //   This means that the value being matched must be a string and the value extracted is also a string
  3. scala>object SingleParamExtractor {
  4.      | defunapply(v:String):Option[String] = if(v.contains("Hello")) Some("Hello") else None
  5.      | }
  6. defined module SingleParamExtractor
  7. // this Will match since the extractor will return a Some object
  8. scala>"Hello World"match { case SingleParamExtractor(v) => println(v) }
  9. Hello
  10. // this will not match and therefore an exception will be thrown
  11. scala>"Hi World"match { case SingleParamExtractor(v) => println(v) }   
  12. scala.MatchError: Hi World
  13.                   at .(:7)
  14.                   at .()
  15.                   at RequestResult$.(:3)
  16.                   at RequestResult$.()
  17.                   at RequestResult$result()
  18.                   at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
  19.                   at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
  20.                   at sun.reflect.DelegatingMethodAccessorImpl.invok...
  21. // This extractor converts the string to an int if possible.
  22. scala>object ConvertToInt{         
  23.      | defunapply(v:String):Option[Int] = try{ Some(v.toInt) } catch { case _ => None }
  24.      | }
  25. defined module ConvertToInt
  26. scala>"10"match { case ConvertToInt(i) => println(i)}
  27. 10
  28. // If you want to extract multiple elements you return an Option that contains a Tuple.  
  29. //   In this example we divide the string into two parts if it has a space
  30. scala>object MultipleParamExtractor {                                       
  31.      |  defunapply(v:String):Option[(String,String)] = (v indexOf ' ') match {           
  32.      | case x if (x>0) => Some ((v take x, v drop x+1))
  33.      | case _ => None
  34.      | }
  35.      | }
  36. defined module MultipleParamExtractor
  37. scala>"hello everyone :)"match { case MultipleParamExtractor(one, two) => println(one,two) }
  38. (hello,everyone :))
  39. // Any object with a unapply method can be used it does not have to be defined as an object
  40. // So if you have a class of extractors that needs to be parameterized you can 
  41. // create a class and use instances of that class for matching
  42. scala>class Splitter(sep:Char){
  43.      | defunapply(v:String):Option[(String,String)] = (v indexOf sep) match {
  44.      | case x if (x>0) => Some ((v take x, v drop x+1))
  45.      | case _ => None
  46.      | }
  47.      | }
  48. defined class Splitter
  49. // Remember that we need the matching object start with an uppercase
  50. // See http://daily-scala.blogspot.com/2009/09/case-sensitive-matching.html 
  51. // for details
  52. scala>val SplitOnComma = new Splitter (',')
  53. SplitOnComma: Splitter = Splitter@15eb9b0d
  54. // How cool now can create splitters for all sorts of things
  55. scala>"1,2"match { case SplitOnComma(one,two) => println(one,two)}
  56. (1,2)
  57. // All extractors can also be used in assignments
  58. scala>val SplitOnComma(one,two) = "1,2"                           
  59. one: String = 1
  60. two: String = 2

3 comments:

  1. Hi there, awesome site. I thought the topics you posted on were very interesting. I tried to add your RSS to my feed reader and it a few. take a look at it, hopefully I can add you and follow.

    ReplyDelete
  2. Awesome !

    Thanks for showing a very useful and simple use case for Scala extractor / unapply! I've been learning how to use this powerful construct.

    ReplyDelete
  3. cool, especially the thing about instances of a class with unapply method

    ReplyDelete