Wednesday, March 10, 2010

How to reverse a map

Suppose you wish to take a map and swap the keys with values. The stackoverflow question Elegant way to revers a map in scala offers some good suggestions

  1. scala> val nodupes = Map(1 -> "a", 2-> "b", 3 -> "c")
  2. nodupes: scala.collection.immutable.Map[Int,java.lang.String] = Map((1,a), (2,b), (3,c))
  3. // Scala 2.8+
  4. scala> nodupes map {_.swap}                          
  5. res4: scala.collection.immutable.Map[java.lang.String,Int] = Map((a,1), (b,2), (c,3))
  6. // Scala 2.7
  7. scala> Map() ++ (nodupes map {case (k,v) => (v,k)})  
  8. res5: scala.collection.immutable.Map[java.lang.String,Int] = Map((a,1), (b,2), (c,3))
  9. // watch out if the values have duplicates you will loose information:
  10. scala> val dupes = Map(1 -> "a", 2-> "b", 3 -> "b")  
  11. dupes: scala.collection.immutable.Map[Int,java.lang.String] = Map((1,a), (2,b), (3,b))
  12. scala> dupes map {_.swap}                          
  13. res6: scala.collection.immutable.Map[java.lang.String,Int] = Map((a,1), (b,3))
  14. // a way to not loose any data
  15. scala> dupes groupBy {_._2} map {case (key,value) => (key, value.unzip._1)}     
  16. res12: scala.collection.Map[java.lang.String,scala.collection.immutable.Iterable[Int]] = Map((a,List(1)), (b,List(2, 3)))
  17. // I wanted to do the following for performance:
  18. scala> dupes.view groupBy {_._2} map {case (key,value) => (key, value.unzip._1)}
  19. java.lang.UnsupportedOperationException: IterableView((1,a), (2,b), (3,b)).newBuilder
  20. at scala.collection.TraversableViewLike$class.newBuilder(TraversableViewLike.scala:40)
  21. at scala.collection.IterableLike$$anon$1.newBuilder(IterableLike.scala:363)
  22. at scala.collection.TraversableLike$$anonfun$groupBy$1.apply(TraversableLike.scala:370)
  23. // but as you can see a view cannot yet be grouped.  Perhaps in the future.

Monday, March 8, 2010

Lazy man's random test data

A quick tip for generating some random testdata.

Note: This is a poor man's solution to using ScalaCheck. If you can handle the dependency I would really recommend using that library.

  1. scala> object Options extends Enumeration {                       
  2.      | val ONE, TWO, THREE, FOUR = Value                          
  3.      | }
  4. defined module Options
  5. /*
  6. Randomly select zero or more elements from the options enumeration
  7. */
  8. scala> Options.values.filter {_ => util.Random.nextBoolean} mkString ", "
  9. res2: String = TWO, FOUR
  10. /*
  11. Select a random string.  
  12. Warning:  there is no restriction on the characters so control characters are likely
  13. */
  14. scala> util.Random.nextString(10)
  15. res5: String = ??????????
  16. /*
  17. ASCII string is oftern more useful for test data.  This selects a random string up to 13 characters long
  18. */
  19. scala> util.Random.nextASCIIString(13)
  20. res6: java.lang.String = RVPD\#_HqJ8:o
  21. /*
  22. This creates a sequence of 10 random strings
  23. */
  24. scala> 1 to 10 map {_ => util.Random.nextASCIIString(13)}
  25. res7: scala.collection.immutable.IndexedSeq[java.lang.String] = IndexedSeq(;E8|Q8H8RI;Q=, vM-X;"ksBr\:c, SKyz{uXNQ5E]X, =Jd8_ll08)s%e, gRCs)6wj%C-YF, `x;2Zru?l*c%@, XE*/Rx9:qPfpm, s|u,e.un+-Xm(, M,TpX9Dq-6$+^, w;exER&#0|}Ya)

Saturday, March 6, 2010

Blocks within if statements

his is another topic that examines the consistency of Scala. This topic examines blocks in if statements. It is related to Blocks within for comprehensions and Temporary Variables during object creation.
  1. // standard if
  2. if(1 > 2) -1 else 0
  3. // since blocks return a value you can use a block within the if statement 
  4. // (not sure when you would want to but...)
  5. if ({ val x = 1
  6.       val y = 2
  7.       x == y }) 1 else 2

Thursday, March 4, 2010

Zip with a constant value

A simple tip for zipping a List (or other collection) with a single value.

  1. scala> Stream.continually("h") zip List(1,2,3,4)
  2. res2: scala.collection.immutable.Stream[(java.lang.String, Int)] = Stream((h,1), ?)
  3. scala> res2 mkString ","
  4. res3: String = (h,1),(h,2),(h,3),(h,4)
  5. scala> List(1,2,3,4) zip Stream.continually("h")
  6. res4: List[(Int, java.lang.String)] = List((1,h), (2,h), (3,h), (4,h))

Wednesday, March 3, 2010

Functions using case statements

A further tip regarding using case statements to construct functions. If a case statement is assigned to a Function it will construct a Function object not a PartialFunction.

I suppose the question is why do you care about this since PartialFunction is a Function. The fact is that a PartialFunction is a Function1. But using a case statement you can construct a Function4 very easily.
  1. scala> def run(f : Function1[Any,Unit]) = println(f.isInstanceOf[PartialFunction[_,_]])
  2. run: (f: (Any) => Unit)Unit
  3. /*
  4. since run expects a Function calling run as shown here will make a 
  5. Function object not a PartialFunction Object
  6. */
  7. scala> run({case f => ()}) 
  8. false
  9. scala> def pf(f : PartialFunction[Any,Unit]) = println(f.isInstanceOf[PartialFunction[_,_]]) 
  10. pf: (f: PartialFunction[Any,Unit])Unit
  11. // Now a PartialFunction will be created
  12. scala> pf({case f => ()})                                                                         
  13. true
  14. scala> def run(f : Function2[Int,String,Unit]) = f(1,"2")                                     
  15. run: (f: (Int, String) => Unit)Unit
  16. /*
  17. This demonstrates why it is important that a case creates a Function
  18. when assigned to a Function. PartialFunctions are Function1 objects
  19. but the following statement is creating a Function2 object.
  20. */
  21. scala> run({                 
  22.      | case (1,b) => println(b)
  23.      | case (a,b) => println(a,b)
  24.      | })
  25. 2

Tuesday, March 2, 2010

Methods on PartialFunction

This topic inspects the methods defined in the PartialFunction Object.
  1. scala> type PF = PartialFunction[Int,Int]
  2. defined type alias PF
  3. // the two partial functions that we will use for the examples
  4. scala> val pf1 : PF = {case 1 => 2}                      
  5. pf1: PF = < function1>
  6. scala> val pf2 : PF = {case 2 => 3}                      
  7. pf2: PF = < function1>
  8. /*
  9. As is well known, when a PartialFunction is called with a value
  10. it must be defined at that value or bad things will happen
  11. */
  12. scala> pf1 isDefinedAt 1  
  13. res14: Boolean = true
  14. scala> pf1 isDefinedAt 2
  15. res15: Boolean = false
  16. scala> pf1(2)
  17. scala.MatchError: 2
  18. at $anonfun$1.apply(< console>:5)
  19. at $anonfun$1.apply(< console>:5)
  20. at .< init>(< console>:7)
  21.     ...
  22. scala> pf1(1)
  23. res5: Int = 2
  24. /*
  25. It is possible to compose two partial functions so first one partialFunction is called and then the next
  26. */
  27. scala> (pf1 andThen pf2) isDefinedAt 2 
  28. res16: Boolean = false
  29. scala> (pf1 andThen pf2) isDefinedAt 1
  30. res17: Boolean = true
  31. scala> (pf1 andThen pf2)(2)
  32. scala.MatchError: 2
  33. at $anonfun$1.apply(< console>:5)
  34. at $anonfun$1.apply(< console>:5)
  35. at scala.PartialFunction$$anon$2.apply(PartialFunction.scala:59)
  36. at .< init>(< console>:8)
  37.     ...
  38. scala> (pf1 andThen pf2)(1)
  39. res8: Int = 3
  40. /*
  41. An alternative way of combining PartialFunctions is to 'or' them
  42. */
  43. scala> (pf1 orElse pf2) isDefinedAt 1
  44. res18: Boolean = true
  45. scala> (pf1 orElse pf2) isDefinedAt 2
  46. res19: Boolean = true
  47. scala> (pf1 orElse pf2) isDefinedAt 3
  48. res20: Boolean = false
  49. scala> (pf1 orElse pf2)(1) 
  50. res9: Int = 2
  51. scala> (pf1 orElse pf2)(2)
  52. res10: Int = 3
  53. /*
  54. Finally a PartialFunction can be easily converted to a function that returns 
  55. an Option
  56. */
  57. scala> pf1.lift
  58. res21: (Int) => Option[Int] = < function1>
  59. scala> pf1.lift(1)
  60. res11: Option[Int] = Some(2)
  61. scala> pf1.lift(2)
  62. res12: Option[Int] = None

Monday, March 1, 2010

NullPointer when mixed traits (Warning)

This tip is mainly to document a 'GOTCHA' that I got caught by recently. It basically goes like this:

Trait Y extends(or has self-type) X. Trait X defines some abstract method 'm'. The initialization code in Y accesses 'm'. Creation of an object new X with Y results in: *Boom* NullPointerException (on object creation).

The example in code:
  1. scala> trait X { val x : java.io.File }
  2. defined trait X
  3. scala> trait Y {self : X => ; val y = x.getName} 
  4. defined trait Y
  5. scala> new X with Y { val x = new java.io.File("hi")}
  6. java.lang.NullPointerException
  7. at Y$class.$init$(< console>:5)
  8. at $anon$1.< init>(< console>:7)
  9. ...

At a glance it seems that x should override the abstract value x in trait X. However the order in which traits are declared is important. In this case first Y is configured then X. Since X is not yet configured Y throws an exception. There are several ways to work around this.
Option 1:
  1. trait X {val x : java.io.File}
  2. trait Y {self : X => ; val y = x.getName}
  3. /*
  4. Declaring Y with X will work because Y is initialized after X
  5. but remember that there may
  6. be other reasons that X with Y is required.  
  7. Method resolution is one such reason
  8. */
  9. new Y with X { val x = new java.io.File("hi")}

Option 2:
  1. trait X { val x : java.io.File }
  2. trait Y {self : X => ; def y = x.getName}
  3. /*
  4. Since method y is a 'def' x.getName will not be executed during initialization.
  5. */
  6. scala> new X with Y { val x = new java.io.File("hi")}
  7. res10: java.lang.Object with X with Y = $anon$1@7cb9e9a3

Option 3:
  1. trait X { val x : java.io.File }
  2. trait Y {self : X => ; lazy val y = x.getName}
  3. /*
  4. 'lazy val' works for the same reason 'def' works: x.getName is not invoked during initialization
  5. */
  6. scala> new X with Y { val x = new java.io.File("hi")}
  7. res10: java.lang.Object with X with Y = $anon$1@7cb9e9a3

Option 4:
  1. trait X {val x : java.io.File }
  2. trait Y extends X {def y = x.getName}
  3. /*
  4. if Y extends X then a new Y can be instantiated
  5. */
  6. new Y {val x = new java.io.File("hi")}

Two more warnings. First, the same error will occur whether 'x' is a def or a val or a var.
  1. trait X { def x : java.io.File }   
  2. trait Y {self : X => ; val y = x.getName}     
  3. new X with Y { val x = new java.io.File("hi")}

Second warning: In complex domain models it is easy to have a case where Y extends X but the final object is created as: new X with Y{...}.

You will get the same error here because (I think) the compiler recognized that Y is being mixed in with X and therefore the X will be initialized as after Y instead of before Y.

First the code:
  1. trait X { def x : java.io.File }   
  2. trait Y extends X { val y = x.getName}        
  3. new X with Y { val x = new java.io.File("hi")}

If the code instantiated new Y{...} the initialization would be X then Y. Because X can only be initialized once, the explicit declaration of new X with Y forces Y to be initialized before X. (X can only be initialized once even when it appears twice in the hierarchy).

This is a topic called linearization and will be addressed in the future.