Showing posts with label tuple. Show all posts
Showing posts with label tuple. Show all posts

Saturday, December 26, 2009

Product

As a response to a comment on a previous post about about Tuples this topic is about Products and the most common subclasses of Tuples.

I should preface this post by saying that I am not going to attempt any scholastic style discussions, instead I am going to try to explain Product in practical terms and stay away from theory. That said on can think of Products as (potentially) heterogenous collections (non-resizable collections). There are several Product classes (Product, Product1, Product2...). All Product classes extend Product, which contains the common methods. Product has the methods for accessing the Product members and the subclass adds type information about the Members.

A good way to think about Product is to look at Tuple which is a Product. There is a good stack overflow question that is worth looking at.

To give more context consider other well known subclasses of Product:
  • All case classes
  • List
  • Option

  1. scala> val product : Product = (1,'2',3)
  2. product: Product = (1,2,3)
  3. scala> product.productIterator.foreach {println _}
  4. 1
  5. 2
  6. 3
  7. scala> product.productArity                       
  8. res1: Int = 3
  9. scala> product.productElement(2)
  10. res2: Any = 3
  11. scala> product.productPrefix    
  12. res3: java.lang.String = Tuple3
  13. scala> product.toString
  14. res4: java.lang.String = (1,2,3)
  15. scala> val product3 = product.asInstanceOf[Product3[Int,Char,Int]]
  16. product3: Product3[Int,Char,Int] = (1,2,3)
  17. scala> product3._2
  18. res5: Char = 2
  19. scala> product3._3
  20. res6: Int = 3
  21. scala> case class Test(name:String, passed:Boolean, error:String)
  22. defined class Test
  23. scala> Test("Chicken Little"false"the sky is falling")
  24. res7: Test = Test(Chicken Little,false,the sky is falling)
  25. scala> res7.productArity
  26. res8: Int = 3
  27. scala> res7.productIterator mkString ", "   
  28. res9: String = Chicken Little, false, the sky is falling

Friday, December 18, 2009

Tuples are (not?) Collections

Tuples are quite handy but a potential annoyance is that at a glance they seem list-like but appearances can be deceiving. The normal collection methods are not supported by Tuples. For example suppose you like the simple syntax: (1,2,3) better than List(1,2,3). With the Tuple you cannot do the maps, filters, etc... (with good reason) but there is a use-case for being able to convert a Tuple to a Traversable.

Word of warning. Unlike collections Tuples are very often not homogeneous. IE you do not have Tuple2[Int] you have Tuple2[A,B]. So the best you can do is to map a tuple to an Iterator[Any].

Note: this was done with Scala 2.8 so results may be slightly different with 2.7. But I believe the syntax is valid.
  1. // the productIterator method gives access to an iterator over the elements
  2. scala> (1,2,3).productIterator.map {_.toString} mkString (",")
  3. res1: String = 1,2,3
  4. scala> (1,2,3).productIterator foreach {println _}            
  5. 1
  6. 2
  7. 3
  8. // if you know the tuple is heterogeneous then you can use partial functions
  9. // for casting the elements to a particular type.  
  10. scala> (1,2,3).productIterator map {case i:Int => i + 2} foreach {println _}
  11. 3
  12. 4
  13. 5
  14. // To get a full Traversable out of the deal you use one of the many
  15. // to* methods to convert to Seq, List, Array, etc...
  16. scala> (1,2,3).productIterator.toList map {case i:Int => i + 2}      
  17. res15: List[Int] = List(3, 4, 5)
  18. // and if you want you can use an implicit to clean up the syntax a bit
  19. // Problem with this is you need an implicit for each Tuple length
  20. scala> implicit def tupleToTraversable[T](t:(T,T,T)) = t.productIterator.toList map { case e:T => e}
  21. warning: there were unchecked warnings; re-run with -unchecked for details
  22. tupleToTraversable: [T](t: (T, T, T))List[T]
  23. scala> (1,2,3) foreach {println _}
  24. 1
  25. 2
  26. 3
  27. /* 
  28. EDIT:  Dan pointed out that the methods I am using are inherited from the
  29. Product super class of Tuple.  So you can do something similar as follows.
  30. Note:  I am using the same name as the previous method so that they don't interfer 
  31. with one another
  32. */
  33. scala> implicit def tupleToTraversable[T](t:Product) = t.productIterator.toList map { case e:T => e} 
  34. warning: there were unchecked warnings; re-run with -unchecked for details
  35. tupleToTraversable: [T](t: Product)List[T]
  36. scala> (1,2,3) foreach {println _}
  37. 1
  38. 2
  39. 3
  40. // this is interesting... it does cast to int unless required
  41. scala> (1,2,'t') foreach {println _}
  42. 1
  43. 2
  44. t
  45. // lets verify we are getting correct conversion
  46. scala> def inc(l:List[Int]) = l map {_ + 1} 
  47. inc: (l: List[Int])List[Int]
  48. scala> inc ( (1,2,3))                              
  49. res4: List[Int] = List(2, 3, 4)
  50. // yep this I expected
  51. scala> inc ( (1,2,'t'))
  52. java.lang.ClassCastException: java.lang.Character cannot be cast to java.lang.Integer
  53. at scala.runtime.BoxesRunTime.unboxToInt(Unknown Source)
  54. at $anonfun$inc$1.apply(< console>:7)
  55. at scala.collection.TraversableLike$$anonfun$map$1.apply(TraversableLike.scala:238)
  56. at .< clinit>(< console>)
  57. scala> def inc(l:List[Int]) = l foreach {println _}
  58. inc: (l: List[Int])Unit
  59. scala> def p(l:List[Int]) = l foreach {println _}  
  60. p: (l: List[Int])Unit
  61. scala> p ( (1,2,'t'))                              
  62. 1
  63. 2
  64. t

Tuesday, November 24, 2009

Equals, Tuples and Case-classes

This is mostly a quick tip (or opportunity for refactoring).

Most of Scala's built in classes implement a useful equals and hashcode. The very commonly used case-classes and Tuple classes are the examples that spring to mind. So this enables the following options:
  1. // Assume Box is out of your control and you cannot refactor it into a case class
  2. scala> class Box(val name:Stringval minx:Intval miny:Intval maxx:Intval maxy:Int)
  3. defined class Box
  4. scala> val box = new Box("mybox", 0, 0, 10, 10)
  5. box: Box = Box@568bf3ec
  6. // before:
  7. scala> box.minx == 0 && box.miny == 0 && box.maxx == 10 && box.maxy == 10      
  8. res3: Boolean = true
  9. // after
  10. scala> import box._
  11. import box._
  12. scala> (minx,miny,maxx,maxy) == (0,0,10,10)
  13. res5: Boolean = true
  14. // another box definition:
  15. scala> case class Box2 (name:String, ul:(Int,Int), lr:(Int,Int)) 
  16. defined class Box2
  17. // case classes have nice equals for comparison
  18. scala> box2 == Box2("a nicer box", (0,0), (10,10))
  19. res6: Boolean = true
  20. // but what if you don't want to compare names
  21. scala> import box2._
  22. import box2._
  23. scala> (ul,lr) == ((0,0),(10,10))
  24. res7: Boolean = true

Thursday, November 5, 2009

Function._

If you have not looked at the Function object I would recommend you do. It contains several useful methods for combining and converting functions.

Examples:
  1. cala> def twoParamFunc( i:Int, j:String) = println(i,j)
  2. twoParamFunc: (i: Int,j: String)Unit
  3. // the tupled method converts the function with n parameters into
  4. // a function that takes one tupleN
  5. scala> val tupled = Function tupled twoParamFunc _
  6. tupled: ((IntString)) => Unit = < function1>
  7. scala> tupled (1 -> "one"
  8. (1,one)
  9. // A practical use-case is to convert an existing method
  10. // for use with a Map
  11. // Example with out using the tupled method
  12. scala> Map( 1 -> "one", 2 -> "two") map (entry => twoParamFunc(entry._1, entry._2)) 
  13. (1,one)
  14. (2,two)
  15. res5: scala.collection.immutable.Iterable[Unit] = List((), ())
  16. // example using tupled
  17. scala> Map( 1 -> "one", 2 -> "two") map (tupled)  
  18. (1,one)
  19. (2,two)
  20. res7: scala.collection.immutable.Iterable[Unit] = List((), ())
  21. // and for balance the opposite
  22. scala> val untupled = Function untupled tupleParamFunc _
  23. untupled: (IntString) => Unit = < function2>
  24. scala> untupled(1, "one"
  25. tuple = (1,one)
  26. // Chain is for chaining together an arbitrary number of functions
  27. scala> def inc(i:Int) = i + 1 
  28. inc: (i: Int)Int
  29. scala> def double(i:Int) = i*i
  30. double: (i: Int)Int
  31. scala> def decr(i:Int) = i - 1
  32. decr: (i: Int)Int
  33. scala> (Function chain List(inc _, double _, decr _, double _, inc _, inc _))(3)
  34. res10: Int = 227
  35. // Now examples curries two methods then chains them together
  36. // define basic methods
  37. scala> def inc(step:Int,i:Int) = i + step 
  38. inc: (step: Int,i: Int)Int
  39. scala> def multiply(i:Int, j:Int) = i * j
  40. multiply: (i: Int,j: Int)Int
  41. // Convert them to methods where the first argument is 3
  42. scala> val inc3 = (Function curried inc _)(3)
  43. inc3: (Int) => Int = < function1>
  44. scala> val multiplyBy3 = (Function curried multiply _)(3)
  45. multiplyBy3: (Int) => Int = < function1>
  46. // chain the resulting Function1 objects together and execute chain with parameter 3
  47. // (3 + 3)*3 = 18
  48. scala> chain(List(inc3, multiplyBy3))(3)
  49. res12: Int = 18

Thursday, September 17, 2009

Tuples and the -> implicit method

Defined in the Predef object are several implicit methods. All implicit methods defined in the Predef method are applicable to all programs without importing them. One such method is the -> method. This method is applicable to all objects and creates a Tuple2.

Examples:
  1. // normal way to make a Tuple2
  2. scala> (1,2)
  3. res3: (Int, Int) = (1,2)
  4. // another way to make a Tuple2 using the implicit -> method
  5. scala> 1 -> 2
  6. res2: (Int, Int) = (1,2)
  7. // Creating a map using the normal tuples creation mechanism
  8. scala> Map (("one",1),
  9.      |      ("two",2))
  10. res5: scala.collection.immutable.Map[java.lang.String,Int] = Map(one -> 1, two -> 2)
  11. // -> syntax allows a second "prettier" way of creating the Map
  12. scala> Map("one" -> 1,
  13.      |     "two" -> 2)
  14. res6: scala.collection.immutable.Map[java.lang.String,Int] = Map(one -> 1, two -> 2)

Thursday, September 10, 2009

Assignments

Assignment in Scala follows more or less the same rules as Java and other related rules. There are some differences.

Increment operators do not work (i++). As I understand it the rational is that it is too specific an idiom. There is not an easy way to generalize it. Instead you must use i += 1.

Assignments do not return a value. For example you cannot do val i = j = 2 or while( (i = read(buffer)) > 0 ){...}.

One feature that is fairly unique in Scala is the ability to expand a case-class or other class that has an associated extractor. For details look at the previous topic Assignment and Parameter Objects.

Examples:

  1. scala> val i,j=2
  2. i: Int = 2
  3. j: Int = 2
  4. scala> val (i,j) = (1,2)
  5. i: Int = 1
  6. j: Int = 2
  7. scala> val (i,j,k) = (1,"two",3.0)
  8. i: Int = 1
  9. j: java.lang.String = two
  10. k: Double = 3.0
  11. scala> caseclass Data( name:String, age:Int, weight:Float)
  12. defined class Data
  13. scala> val Data(name, age, weight) = Data("Jesse", 133, 100f)
  14. name: String = Jesse
  15. age: Int = 133
  16. weight: Float = 100.0
  17. scala> val value = 1
  18. value: Int = 1
  19. scala> i += 1
  20. :10: error: reassignment to val
  21.        i += 1
  22.          ^
  23. scala> var variable = 1
  24. variable: Int = 1
  25. scala> variable += 1
  26. scala> println(variable)
  27. 2

Sunday, August 16, 2009

Assignment and Parameter Objects

One of the principle design goals of Scala is to be "deep" not wide, which means the language attempts to have a small set of rules that can be applied in many different ways in different situations. Pattern matching is one of my favourite examples of this. Pattern matching is commonly seen in match { case... } statements but you will also see it frequently in exception handling, function declaration and, most important for this post, assignment.

Scala does not have multiple assignment like some languages. Instead it has tuples and matching. Tuples are a light-weight way of grouping data in a simple data object. (1,2.0,'c',"string", true). A simple example of a 5 element tuple. Tuples can be up to 22 elements long and can be homogenous or heterogenous. Using this for multiple assignement works something like:
  1. val (firstname, lastname) = ("Jesse","Eichar")

This is pattern matching.
  1. scala> ("Jesse","Eichar") match {
  2.      | case (firstname,lastname) => println(firstname,lastname)
  3.      | }
  4. (Jesse,Eichar)

Notice that in both cases you need the brackets around firstname, lastname. This instructs the compiler that you are matching against a Tuple.

Now the interesting use is with parameter objects. Tuples are a poor substitute for parameter objects because they do not have context. Changing:
  1. def myMethod( firstname:String, middlename:String, lastname:String) = {...}

to
  1. def myMethod( name:(String,String,String)) = {...}

Is not a good change because you loose context. What are the 3 strings? The information must go in the Javadocs. A better option:
  1. caseclass Name(first:String, middle:String, last:String)
  2. def myMethod( name:Name ) = {
  3.   val Name(first, middle, last) = name
  4.   // do something with first middle last
  5. }

The beauty is that you have an object that you can pass around easily. It is a case class therefore extracting the information is incredibly easy and unlike a tuple it has context and can have methods added easily.

Yes it is longer to write but if you need to reuse the data in several locations the trade off is well worth it in clarity.

Examples:
  1. // define name data object.
  2. // Notice toString is a lazy val.  This means value is calculated only once.
  3. scala>caseclass Name(first:String, middle:String, last:String) {
  4.      | override lazy val toString="%s, %s %s" format (last, first,middle)
  5.      | }
  6. defined class Name
  7. // toString formats name nicely.
  8. scala> Name("Jesse","Dale","Eichar")
  9. res1: Name = Eichar, Jesse Dale
  10. scala>def readName() = {
  11.      | //maybe load from a file or database
  12.      | Name("Jesse","Dale","Eichar") :: Name("Jody","","Garnett") :: Name("Andrea","","Antonello"):: Nil
  13.      | }
  14. readName: ()List[Name]
  15. scala>def firstLastName(name:Name) = {
  16.      |  // we are putting _ because we don't care about middle name
  17.      | val Name( first, _, last ) = name
  18.      | (first, last)
  19.      | }
  20. firstLastName: (Name)(String, String)
  21. // convert the list of Names to a list of tuples of first and last name
  22. scala> readName().map( firstLastName _ )
  23. res2: List[(String, String)] = List((Jesse,Eichar), (Jody,Garnett), (Andrea,Antonello))
  24. // print all first names starting with J
  25. scala>for( Name(first,_,_) <- readName; if (first.startsWith("J"))) println(first)
  26. Jesse
  27. Jody
  28. // print the first and middle parts of the first name in the list
  29. scala> readName() match {
  30.      | // the :: _ indicates that we are matching against a list but we don't care about the rest of the list
  31.      | case Name(f,m,_) ::  _ => println( f, m)
  32.      | case _ => println("Not a list of names")
  33.      | }
  34. (Jesse,Dale)