Wednesday, March 31, 2010

Variant Positions 2

This is a continuation of post: Variant Positions 1

...

My first attempt at the Verified was to make it a mutable object (my Java tourettes kicking in). But it cannot be covariant and mutable. Look at the code to see why:
`class Verified[+A <: V,V](assertion : (V) => Boolean, private var value : A){    assert(assertion(value))        def a = value// update is illegal.  See the example below    def update[ B >: A <: V](a : B) = value = a}def notNull(obj : AnyRef) = obj != nullval v = new Verified(notNull, "hi")/*Up to this point everything looks ok but the next linewill assign an object to value which is a reference to a String*/v update (new Object())`
For Verified to be mutable A must be invariant. If you look at the Mutable collections in Scala they are all invariant.

Here is an interesting example of both invariant and covariant type parameters in a class hierarchy:
`scala> class X[+A](val x :A)defined class Xscala> class Y[A](var a: A) extends X[A](a)defined class Yscala> val x: X[Any] = new Y[String]("hi")x: X[Any] = Y@1732a4dfscala> x.asInstanceOf[Y[String]].a="ho"`
This example is perfectly legal because no matter how X[Any] is used no illegal assignment in Y can occur. The interesting thing is that the object can be used in covariant usecases when only X is required. This is now the collections in Scala can work.

Here is a little example of collections invariance and covariance in action. In List the parameter is covariant but in Buffer it is invariant
`scala> def printList(l : List[Any]) = print(l mkString " :: ")printList: (l: List[Any])Unitscala> val buffer = Buffer(1,2,3)buffer: scala.collection.mutable.Buffer[Int] = ArrayBuffer(1, 2, 3)scala> printList(buffer)1 :: 2 :: 3/*++ is part of Iterable.  Since Iterable is covariant ++ returns a new buffer it does not modify the existing bufferAll mutators are only defined on invariant traits*/scala> buffer ++ List(4)res16: scala.collection.mutable.Buffer[Int] = ArrayBuffer(1, 2, 3, 4)scala> res16 eq bufferres17: Boolean = false/*buffer defines += to add an element to the bufferso res27 is the same buffer with an extra element*/scala> buffer += 10res27: buffer.type = ArrayBuffer(1, 2, 3, 10)scala> res27 eq bufferres28: Boolean = true`

Tuesday, March 30, 2010

Variant Positions 1

An additional topic on variance to finish up the major points on the topic. The previous two posts: contain required information for following this post.

In-, co- and contra- variance are the three types of variance expressible in Scala. I showed how this affects assignments and arguments being pass to methods in the last two topics. This looks at how the different types of variance influences how classes can be defined. In the last post we saw how the compiler complained about a method definition in a covariant class because the compiler recognized that such a definition was inherently dangerous and must be prohibited. The example was:
`scala> class Output[+A] {def write(a : A) = () /*do write*/ }< console>:5: error: covariant type A occurs in contravariant position in type A of value a       class Output[+A] {def write(a : A) = () /*do write*/ }                                   ^`
For an class like Output it does not make sense to have A be covariant so we changed A to be contravariant. However suppose we have a collection type class.
`class Verified[+A] (assertion : (A) => Boolean, value : A){    assert(assertion(value))        def a = value    def a_=(a : A) = new Verified(assertion, a)}`
The previous definition is not legal because value and a in the parameter of a_= "occur in a contravariant position." What to do? Making A contravariant isn't an option:
`class Verified[+A <: V,V](assertion : (V) => Boolean, val value : A){    assert(assertion(value))/*this is the key.  Restrict possible types ofA Since B is a super (or equal) type of A*/    def update[ B >: A <: V](a : B) = new Verified(assertion, a)}// example useagescala> def notNull(obj : AnyRef) = obj != nullnotNull: (obj: AnyRef)Booleanscala> val v = new Verified(notNull, "hi")v: Verified[java.lang.String,AnyRef] = Verified@307b37dfscala> val newV = v update (new Object())newV: Verified[java.lang.Object,AnyRef] = Verified@36f72f09// 3 is not legal because the type parameter 'V' is AnyRef.  Int is a subclass of Any NOT AnyRefscala> val newV = v update (3)           < console>:8: error: inferred type arguments [Any] do not conform to method update's type parameter bounds [B >: java.lang.String <: AnyRef]       val newV = v update (3)                  ^`

Saturday, March 27, 2010

Contravariance

Continuing on with variance and type parameters, this topic will discuss contravariance. See the post In- and Co- variance of type parameters for the intro material required for this topic.

Covariant parameters allow for an additional dimension of type compatibility:
`val l : List[Object] = List("this is legal")`
Contravariance provides the opposite:
`// If the type parameter of list was contravariant this would be legal:val l : List[String] = List(new Object())`
As covariance is indicated by a '+' before the type contravariance is indicated by a '-'
`scala> class X[-A]defined class Xscala> val l : X[String] = new X[Object]l: X[String] = X@66201d6d`
I can almost hear the "cool... but why?". Following the lead in the Programming In Scala book. Consider OutputStream first and a method in a Collection second. (The following code is illegal but consider it)
`class Output[+A] {def write(a : A) = () /*do write*/ }def writeObject(out : Output[Object]) = out.write("hello")/*Uh oh you this only is for outputting lists not Objects (certainly not the String that is actually written)Runtime error for sure!*/writeObject(new Output[List[String]])`
The previous example (if it would compile) would explode because an Output that can only write lists is passed to the method. In the example a String is written to the Output object. The Output[List[String]] cannot handle that.

Fortunately the compiler sees the definition of the class and recognizes this is an error waiting to happen and makes it illegal:
`scala> class Output[+A] {def write(a : A) = () /*do write*/ }< console>:5: error: covariant type A occurs in contravariant position in type A of value a       class Output[+A] {def write(a : A) = () /*do write*/ }                                   ^`
Consider the implications of making A contravariant?
`// The definition of object is now legalclass Output[-A] {def write(a : A) = () /*do write*/ }// this is now a safe method definition since the parameter of Output must be a Object or a super classdef writeObject(out : Output[Object]) = out.write("hello")// Now this is illegal as it should bescala> writeObject(new Output[List[String]])< console>:8: error: type mismatch; found   : Output[List[String]] required: Output[java.lang.Object]       writeObject(new Output[List[String]])       // this is legal... scala> writeObject(new Output[Any])`
In this example Output[Any] can be passed to the method. This makes sense. If the Output object knows how to write Any oject then it knows how to write an Object; its all good.

Wednesday, March 24, 2010

In- and Co- variance of type parameters

In Java most parameterized types are considered to be "invariant". What does that mean? Here is an example to explain:
`/*This is an example of a parameterized class that with an invariant parameter BIn both Scala and Java parameters are invariant by default.*/scala> class Invariant[B]defined class Invariantscala> var x : Invariant[Object] = new Invariant[Object]x: Invariant[java.lang.Object] = Invariant@2e0c5575/*Note: Invariant[String] cannot be assigned to Invariant[Object]      even though logically it seems like it should be.      This is the effect of invariance.  Covariant parameters do not have      this restriction.*/scala> var x : Invariant[Object] = new Invariant[String]< console>:6: error: type mismatch; found   : Invariant[String] required: Invariant[java.lang.Object] var x : Invariant[Object] = new Invariant[String]                             ^scala> class Sub[A] extends Invariant[A]   defined class Sub/*Since Sub is a subclass of Invariant it can be assigned(but not Sub[String])*/scala> val x : Invariant[Object] = new Sub[Object]x: Invariant[java.lang.Object] = Sub@26ced1a8`
Assignment compatibility has multiple dimensions: the object type and the types of the parameters. Unlike object type the compatibility of the type-parameters can be covariant, contravariant and invariant. Java has invariant parameters and that is demonstrated by the previous example. Covariant parameters allow subclassing. Contravariant parameters need their own topic.
`// The '+' indicates the parameter is covariantscala> class Covariant[+B]defined class Covariantscala> var x : Covariant[Object] = new Covariant[Object]x: Covariant[java.lang.Object] = Covariant@315cb235// Now this is legalscala> var x : Covariant[Object] = new Covariant[String]x: Covariant[java.lang.Object] = Covariant@26e2e276/*Warning: The following is not legal because          you cannot supply an invariant parameter          with a covariant value.*/scala> class Sub[+A] extends Invariant[A]< console>:7: error: covariant type A occurs in invariant position in type [+A]Invariant[A] with ScalaObject{def this(): Sub[A]} of class Sub       class Sub[+A] extends Invariant[A]             ^scala> class Sub[+A] extends Covariant[A]defined class Subscala> class Sub[A] extends Covariant[A] defined class Sub`

Monday, March 22, 2010

Implicit '=' operator

Continuing on with operators, There is a special type of operator in Scala. It is an operator that ends with =. If a class has operation (methods with an operator identifer) the = can be appended to the effectively creating a new method. In truth a new method is not created instead the compiler rewrites the line.

For example. If a method (like Int) defines + then a method call += can be used. It can be used to mutate a variable:
`scala> var i = 1i: Int = 1scala> i += 1scala> ires3: Int = 2`
To illustrate this is not a special case for Int the next example defines several operations and demonstrates in place variable mutation.
`scala> case class MyClass(i:Int) {           | def +(j:Int) = new MyClass(j + i)     | def -(j:Int) = new MyClass(i - j)     | def ^(j:Int) = MyClass(j)     | def +|(j:Int) = new MyClass(j + i / 3)     | }defined class MyClassscala> var c = MyClass(1)c: MyClass = MyClass(1)scala> c+=6scala> cres5: MyClass = MyClass(7)scala> c -= 2scala> cres7: MyClass = MyClass(5)scala> c ^= 10scala> cres23: MyClass = MyClass(10)scala> c +|= 5scala> cres25: MyClass = MyClass(8)`
Here are several more examples using existing classes in Scala. They are all immutable examples.
`scala> var l = Set(1,2,3) l: scala.collection.immutable.Set[Int] = Set(1, 2, 3)scala> l += 10scala> lres7: scala.collection.immutable.Set[Int] = Set(1, 2, 3, 10)scala> var seq = Seq(5,6,3)seq: Seq[Int] = List(5, 6, 3)scala> seq :+= 10scala> seq                 res9: Seq[Int] = List(5, 6, 3, 10)scala> seq +:= 10   scala> seq       res11: Seq[Int] = List(10, 5, 6, 3, 10)scala> var list = List(32)list: List[Int] = List(32)scala> list ::= 12scala> listres13: List[Int] = List(12, 32)`
Note: assignment operators can also be defined as methods to mutate an object
`scala> case class MyClass(var i:Int) {     | def += (j:Int) = { i+=j ; this }     | }defined class MyClassscala> val m = MyClass(6)m: MyClass = MyClass(6)scala> m += 7res0: MyClass = MyClass(13)scala> m += 9res1: MyClass = MyClass(22)scala> res1 eq mres2: Boolean = true`

Friday, March 19, 2010

Operators

Since Scala allows one to define the behavior of operators there are some rules involving operators and assignment like +=. One of the standard method calls in most languages is `i += 1.`

Since `i+=1`(no spaces) is also valid, Scala has some rules regarding how statements like `i+=1` should be broken up. Obviously we know it should be 'i' '+=' and '1'. So there is a special class of characters called operators. I don't know all of them but a few are: `+ - ^ * / % ! | & =`( ':' is sort of part of this group but has some special properties as well).

These characters can be method names but they cannot be combined with other identifier characters.

Update: These characters can be combined with other identifier characters if there is an under score so:
`def x+ = 3   // not validdef x_+ = 3  // validdef +x = 3   // not valid`
However these characters are special because they can be combined in a special way with '=' for a special assignment construct as shown in the next post.

(end update)

`scala> case class MyClass(i:Int) {     | def +(j:Int) = new MyClass(j + i)     | def -(j:Int) = new MyClass(i - j)     | def ^(j:Int) = MyClass(j)     | def +|(j:Int) = new MyClass(j + i / 3)     | }  scala> val c = MyClass(3) c: MyClass = MyClass(3) scala> c + 4 res26: MyClass = MyClass(7) scala> c-2  res27: MyClass = MyClass(1) scala> c -6 res28: MyClass = MyClass(-3) scala> c ^ 3 res29: MyClass = MyClass(3) scala> c+|5 res31: MyClass = MyClass(6)`

Wednesday, March 17, 2010

Geoscript.scala

This is a bit of a departure from the standard daily-scala format, but it is one of the elements I had always invisioned. This topic takes a look at Geoscript.scala. (Github repo is at: http://github.com/dwins/geoscript.scala)

Geoscript has been given a Scala implementation. It is based on the Geotools Java library and thus far provides a script interface to access some of the basic spatial functions.

For setting up the console with the required libraries follow the instructions at: http://geoscript.org/scala/quickstart.html#quickstart

Here is an example of Geoscript in action:

`scala> import org.geoscript.GeoScript._import org.geoscript.GeoScript._scala> import org.geoscript.geometry._import org.geoscript.geometry._scala> val line = LineString((10, 10), (20, 20), (30, 40))line: org.geoscript.geometry.LineString = LINESTRING (10 10, 20 20, 30 40)/*create a polygon by buffering the line (essentially expanding the line by 10 units that is degrees if not otherwise specified)*/scala> val poly = line buffer 10poly: org.geoscript.geometry.Geometry = POLYGON ((11.781455848733053 25.923591472464004, 21.05572809000084 44.47213595499958, 22.100060210309515 46.13114600374718, 23.447982586398712 47.55453954995706, 25.04769531727891 48.68761637789669, 26.837722339831622 49.48683298050514, 28.74927391943886 49.921475911950004, 30.708890200906794 49.97484208812642, 32.64126422950409 49.6448806768120...// query the area of the polygonscala> poly.areares0: Double = 1041.9912814842407// get the centroids of the polygon and linescala> line.centroidres1: org.geoscript.geometry.Point = POINT (21.12574113277207 24.188611699158105)scala> poly.centroidres2: org.geoscript.geometry.Point = POINT (20.79088988611118 24.43096430943361)/*Obviously the polygon and line intersect since the polygon is a buffer of the line*/scala> poly.intersects(line)res3: Boolean = truescala> val poly2 = Geometry.fromWKT("POLYGON ((10 10, 10 20, 20 20, 20 15, 10 10))")poly2: org.geoscript.geometry.Geometry = POLYGON ((10 10, 10 20, 20 20, 20 15, 10 10))// less trivial intersects operationscala> poly intersects poly2res3: Boolean = true// not make a new geometry from the intersection of the two geometriesscala> val intersecting = poly intersection poly2intersecting: org.geoscript.geometry.Geometry = POLYGON ((10 10, 10 20, 20 20, 20 15, 10 10))scala> intersecting.area res6: Double = 75.0scala> import org.geoscript.projection._import org.geoscript.projection._/*None of the previous geometries has a projection associated.  A new geometry can have one created with a projection by using the in(Projection) method*/ scala> val latLongPoly = poly2 in Projection("epsg:4326")latLongPoly: org.geoscript.geometry.Geometry = POLYGON ((10 10, 10 20, 20 20, 20 15, 10 10))// now reproject the latlong projection to a french projection scala> latLongPoly in Projection("epsg:21781")res12: org.geoscript.geometry.Geometry = POLYGON ((950650.7690658928 -4203986.192880551, 900363.7533498043 -2900002.601715782, 2061411.5566836582 -2774908.8442438124, 2174910.791185147 -3393231.5380846346, 950650.7690658928 -4203986.192880551))`

Tuesday, March 16, 2010

Assert, Require, Assume

Very simple but useful are the methods assert, require and assume which are built into the Predef object. As you might expect they are methods for performing certain checks during runtime to verify certain conditions. They do not use the Java assert framework and therefore are always evaluated regardless of whether or not assertions are enabled.

Update: Scala 2.8 has an annotation called elidable that will (when 2.8 is complete) allow one to remove method calls at compile time by setting a compiler flag. The assert, etc... methods are all marked with this flag and as a result can be removed at compile time for production environments.

Scala2.8
`scala> var called = 0called: Int = 0scala> calledres0: Int = 0/*assert, require and assume have call by name parameters so the message is only calculated when the assertion fails.*/scala> assert (called == 0, {called += 1; println("called is not 0")})scala> require (called == 0, {called += 1; println("called is not 0")})scala> assume (called == 0, {called += 1; println("called is not 0")}) scala> called = 1called: Int = 1// demonstrating that the method is in fact called when the assertion failsscala> assert (called == 0, {called += 1; println("called is not 0")}) called is not 0java.lang.AssertionError: assertion failed: () at scala.Predef\$.assert(Predef.scala:95) ... scala> calledres4: Int = 2/*Require is intended to be used as a precondition of a method so it throws an IllegalArgumentException, not an AssertionError*/scala> require (called == 0, {called += 1; println("called is not 0")})called is not 0java.lang.IllegalArgumentException: requirement failed: () at scala.Predef\$.require(Predef.scala:117) ... scala> called                                                          res6: Int = 3scala> assume (called == 0, {called += 1; println("called is not 0")}) called is not 0java.lang.AssertionError: assumption failed: () at scala.Predef\$.assume(Predef.scala:107) ... scala> called                                                         res8: Int = 4`

scala 2.7.7
`/*In Scala 2.7 the parameter is evaluated before the method is called so the side effect of the message causesthe assertion to fail*/scala> assert (called == 0, {called += 1; println("called is not 0")})called is not 0scala> calledres2: Int = 1`

Monday, March 15, 2010

Unzip

_ Scala 2.8 only tip _

Unzip is a handy companion to partition.
- Partition divides a traversable into two traversables by a boolean predicate.
- Unzip divides a traversable into two by dividing each element into two parts (each becomes an element in one traversable). If an element is a Tuple2 then each tuple is divided into two otherwise a function is required to divide an element into two.

`// basic usagescala> List((1,2),(2,3)).unzipres2: (List[Int], List[Int]) = (List(1, 2),List(2, 3))/* tuples can be of different types and the resulting traversables reflect the differing types*/scala> List((2,"a"),(3,"b")).unzipres3: (List[Int], List[java.lang.String]) = (List(2, 3),List(a, b))// Maps are Traversable[Collection] so unzip works with themscala> Map(1 -> 2, 3 -> 4).unzipres1: (scala.collection.immutable.Iterable[Int], scala.collection.immutable.Iterable[Int]) = (List(1, 3),List(2, 4))// Of course sets result in sets and duplicates are collected to a single elementscala> Set((1,2),(2,2)).unzipres7: (scala.collection.immutable.Set[Int], scala.collection.immutable.Set[Int]) = (Set(1, 2),Set(2))/*Arbitrary elements can be unziped if a method is provided to decompose each element*/scala> List("one word", "another word").unzip {e => (e takeWhile {_ != ' '}, e dropWhile {_ != ' '})} res6: (List[String], List[String]) = (List(one, another),List( word,  word))/*The following shows the same function applied with map.  It results in a single list of Tuples rather than two lists of single elements */scala> List("one word", "another word").map {e => (e takeWhile {_ != ' '}, e dropWhile {_ != ' '})}  res8: List[(String, String)] = List((one, word), (another, word))`

Friday, March 12, 2010

Multiple Argument Implicit Conversions

Suppose you are creating a DSL and you want to implicitly convert 2 values to a particular object:
`val v : SomeObject = (2, 3)`
This is easily attained. In this example we will support the previous example as well as the standard 1 object to another implicit conversion.
`val v : SomeObject = 2`
Example:
`// first lets define a classscala> case class Randomly(x : Int, y : Double)defined class Randomly// the normal conversionscala> implicit def intToRandomly(i : Int) = new Randomly(i,0.0)intToRandomly: (i: Int)Randomly/*now a tuple for the other conversion.Important:  The two conversions must have different names. At least that is the case in Scala 2.8*/scala> implicit def tupleToRandomly(i : (Int, Double)) = new Randomly(i._1, i._2)tupleToRandomly: (i: (Int, Double))Randomly scala> val r1 : Randomly = 4                                                     r1: Randomly = Randomly(4,0.0)scala> val r2 : Randomly = (4, 6.0)r2: Randomly = Randomly(4,6.0)/*Suppose you want to doval r : Randomly = (4,4)you might think to implicitly convert from in to double*/scala> implicit def intToDouble(i : Int) = i.toDoubleintToDouble: (i: Int)Double// implicit chaining is not permittedscala> val r3 : Randomly = (4, 6)                    < console>:10: error: type mismatch; found   : (Int, Int) required: Randomly       val r3 : Randomly = (4, 6)// Here is the legal optionscala> implicit def intTupleToRandomly(t: (Int,Int)) = new Randomly(t._1,t._2.toDouble) intTupleToRandomly: (t: (Int, Int))Randomlyscala> val r3 : Randomly = (4, 6)                                                      r3: Randomly = Randomly(4,6.0)`

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

`scala> val nodupes = Map(1 -> "a", 2-> "b", 3 -> "c")nodupes: scala.collection.immutable.Map[Int,java.lang.String] = Map((1,a), (2,b), (3,c))// Scala 2.8+scala> nodupes map {_.swap}                          res4: scala.collection.immutable.Map[java.lang.String,Int] = Map((a,1), (b,2), (c,3))// Scala 2.7scala> Map() ++ (nodupes map {case (k,v) => (v,k)})  res5: scala.collection.immutable.Map[java.lang.String,Int] = Map((a,1), (b,2), (c,3))// watch out if the values have duplicates you will loose information:scala> val dupes = Map(1 -> "a", 2-> "b", 3 -> "b")  dupes: scala.collection.immutable.Map[Int,java.lang.String] = Map((1,a), (2,b), (3,b))scala> dupes map {_.swap}                          res6: scala.collection.immutable.Map[java.lang.String,Int] = Map((a,1), (b,3))// a way to not loose any datascala> dupes groupBy {_._2} map {case (key,value) => (key, value.unzip._1)}     res12: scala.collection.Map[java.lang.String,scala.collection.immutable.Iterable[Int]] = Map((a,List(1)), (b,List(2, 3)))// I wanted to do the following for performance:scala> dupes.view groupBy {_._2} map {case (key,value) => (key, value.unzip._1)}java.lang.UnsupportedOperationException: IterableView((1,a), (2,b), (3,b)).newBuilder at scala.collection.TraversableViewLike\$class.newBuilder(TraversableViewLike.scala:40) at scala.collection.IterableLike\$\$anon\$1.newBuilder(IterableLike.scala:363) at scala.collection.TraversableLike\$\$anonfun\$groupBy\$1.apply(TraversableLike.scala:370)// 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.

`scala> object Options extends Enumeration {                            | val ONE, TWO, THREE, FOUR = Value                               | }defined module Options/*Randomly select zero or more elements from the options enumeration*/scala> Options.values.filter {_ => util.Random.nextBoolean} mkString ", "res2: String = TWO, FOUR/*Select a random string.  Warning:  there is no restriction on the characters so control characters are likely*/scala> util.Random.nextString(10)res5: String = ??????????/*ASCII string is oftern more useful for test data.  This selects a random string up to 13 characters long*/scala> util.Random.nextASCIIString(13)res6: java.lang.String = RVPD\#_HqJ8:o/*This creates a sequence of 10 random strings*/scala> 1 to 10 map {_ => util.Random.nextASCIIString(13)}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.
`// standard ifif(1 > 2) -1 else 0// since blocks return a value you can use a block within the if statement // (not sure when you would want to but...)if ({ val x = 1      val y = 2      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.

`scala> Stream.continually("h") zip List(1,2,3,4)res2: scala.collection.immutable.Stream[(java.lang.String, Int)] = Stream((h,1), ?)scala> res2 mkString ","res3: String = (h,1),(h,2),(h,3),(h,4)scala> List(1,2,3,4) zip Stream.continually("h")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.
`scala> def run(f : Function1[Any,Unit]) = println(f.isInstanceOf[PartialFunction[_,_]])run: (f: (Any) => Unit)Unit/*since run expects a Function calling run as shown here will make a Function object not a PartialFunction Object*/scala> run({case f => ()}) falsescala> def pf(f : PartialFunction[Any,Unit]) = println(f.isInstanceOf[PartialFunction[_,_]]) pf: (f: PartialFunction[Any,Unit])Unit// Now a PartialFunction will be createdscala> pf({case f => ()})                                                                         truescala> def run(f : Function2[Int,String,Unit]) = f(1,"2")                                     run: (f: (Int, String) => Unit)Unit/*This demonstrates why it is important that a case creates a Functionwhen assigned to a Function. PartialFunctions are Function1 objectsbut the following statement is creating a Function2 object.*/scala> run({                      | case (1,b) => println(b)     | case (a,b) => println(a,b)     | })2`

Tuesday, March 2, 2010

Methods on PartialFunction

This topic inspects the methods defined in the PartialFunction Object.
`scala> type PF = PartialFunction[Int,Int]defined type alias PF// the two partial functions that we will use for the examplesscala> val pf1 : PF = {case 1 => 2}                      pf1: PF = < function1>scala> val pf2 : PF = {case 2 => 3}                      pf2: PF = < function1>/*As is well known, when a PartialFunction is called with a valueit must be defined at that value or bad things will happen*/scala> pf1 isDefinedAt 1  res14: Boolean = truescala> pf1 isDefinedAt 2res15: Boolean = falsescala> pf1(2)scala.MatchError: 2 at \$anonfun\$1.apply(< console>:5) at \$anonfun\$1.apply(< console>:5) at .< init>(< console>:7)    ...scala> pf1(1)res5: Int = 2/*It is possible to compose two partial functions so first one partialFunction is called and then the next*/scala> (pf1 andThen pf2) isDefinedAt 2 res16: Boolean = falsescala> (pf1 andThen pf2) isDefinedAt 1res17: Boolean = truescala> (pf1 andThen pf2)(2)scala.MatchError: 2 at \$anonfun\$1.apply(< console>:5) at \$anonfun\$1.apply(< console>:5) at scala.PartialFunction\$\$anon\$2.apply(PartialFunction.scala:59) at .< init>(< console>:8)    ...scala> (pf1 andThen pf2)(1)res8: Int = 3/*An alternative way of combining PartialFunctions is to 'or' them*/scala> (pf1 orElse pf2) isDefinedAt 1res18: Boolean = truescala> (pf1 orElse pf2) isDefinedAt 2res19: Boolean = truescala> (pf1 orElse pf2) isDefinedAt 3res20: Boolean = falsescala> (pf1 orElse pf2)(1) res9: Int = 2scala> (pf1 orElse pf2)(2)res10: Int = 3/*Finally a PartialFunction can be easily converted to a function that returns an Option*/scala> pf1.liftres21: (Int) => Option[Int] = < function1>scala> pf1.lift(1)res11: Option[Int] = Some(2)scala> pf1.lift(2)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:
`scala> trait X { val x : java.io.File }defined trait Xscala> trait Y {self : X => ; val y = x.getName} defined trait Yscala> new X with Y { val x = new java.io.File("hi")}java.lang.NullPointerException at Y\$class.\$init\$(< console>:5) at \$anon\$1.< init>(< console>:7) ...`
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:
`trait X {val x : java.io.File}trait Y {self : X => ; val y = x.getName}/*Declaring Y with X will work because Y is initialized after Xbut remember that there maybe other reasons that X with Y is required.  Method resolution is one such reason*/new Y with X { val x = new java.io.File("hi")}`
Option 2:
`trait X { val x : java.io.File }trait Y {self : X => ; def y = x.getName}/*Since method y is a 'def' x.getName will not be executed during initialization.*/scala> new X with Y { val x = new java.io.File("hi")}res10: java.lang.Object with X with Y = \$anon\$1@7cb9e9a3`
Option 3:
`trait X { val x : java.io.File }trait Y {self : X => ; lazy val y = x.getName}/*'lazy val' works for the same reason 'def' works: x.getName is not invoked during initialization*/scala> new X with Y { val x = new java.io.File("hi")}res10: java.lang.Object with X with Y = \$anon\$1@7cb9e9a3`
Option 4:
`trait X {val x : java.io.File }trait Y extends X {def y = x.getName}/*if Y extends X then a new Y can be instantiated*/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.
`trait X { def x : java.io.File }   trait Y {self : X => ; val y = x.getName}     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:
`trait X { def x : java.io.File }   trait Y extends X { val y = x.getName}        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.