Showing posts with label intermediate. Show all posts
Showing posts with label intermediate. Show all posts

Wednesday, May 26, 2010

Return value of a block

A common misunderstanding is that a code block (without parameters) is a function. That is not the case. A code block is a sequence of statements that are executed and result the last statement is returned. That sounds like a Function0, however, if the block is passed to a method/function only the last statement will be returned to the function/method. If that method/function expects a function as the parameter the last statement maybe returned as a function not a value, this means that the block itself is not a function.
  1. scala> var count = 0                                                                                                                         
  2. count: Int = 0
  3. // the last statement is returned as a function so count
  4. // is incremented only one during the creation of the function
  5. scala> List(1,2,3,4).map{count += 1;_ + 1}
  6. res9: List[Int] = List(2, 3, 4, 5)
  7. scala> count
  8. res10: Int = 1
  9. // now the count increment is within the function
  10. scala> List(1,2,3,4).map{i => count += 1;i + 1}
  11. res11: List[Int] = List(2, 3, 4, 5)
  12. scala> count
  13. res12: Int = 5

The previous example demonstrates a Gotcha if I ever saw one. Map expects a function so the block essentially constructs a function. The last statement being the function. The first line count += 1 executed only once because it is part of creating the function not part of the resulting function. This is equivalent to:
  1. scala> val x = {count += 1 ; i:Int => i +1}
  2. x: (Int) => Int = < function1>
  3. scala> List(1,2,3,4).map(x)
  4. res15: List[Int] = List(2, 3, 4, 5)

Beginning a block with the parameter list signals that the entire block is a function.

Rule of thumb: Functions with placeholder parameters should be a single statement.

Thursday, May 20, 2010

Type Inference with Abstract Types

A second "gotcha" that one might get tripped up when dealing with abstract types is the signature of the concrete class contains type information about the abstract type. So if you are not explicit when assigning a variable or defining a function you can get unexpected compiler errors.
  1. scala> trait S {
  2.      |   type x
  3.      |   def get : x
  4.      | }
  5. defined trait S
  6. scala> var sample = new S{ 
  7.      |   type x = Int
  8.      |   def get = 3
  9.      | }
  10. sample: java.lang.Object with S{type x = Int} = $anon$1@397af435
  11. scala> sample = new S {
  12.      |   type x = Double
  13.      |   def get = 3.0
  14.      | }
  15. < console>:7: error: type mismatch;
  16.  found   : java.lang.Object with S
  17.  required: java.lang.Object with S{type x = Int}
  18.        sample = new S {

In this example sample uses type inference so the actual type is S with underlying type Int. The consequence is that sample can only be assigned with instances of S with type x = Int. The fix is to explicitly declare the variable type:
  1. scala> var sample2 : S = new S{ 
  2.      |   type x = Int
  3.      |   def get = 3
  4.      | }
  5. sample2: S = $anon$1@31602bbc
  6. scala> sample2 = new S {
  7.      |   type x = Double
  8.      |   def get = 3.0
  9.      | }
  10. sample2: S = $anon$1@4de5ed7b

The same thing happens when declaring functions and allows type inference for function definition
  1. scala> class Fac {
  2.      |   def newS = new S {
  3.      |     type x = Int
  4.      |     def get = 3
  5.      |   }
  6.      | }
  7. defined class Fac
  8. scala> class SubFac extends Fac{
  9.      |   override def newS = new S {
  10.      |     type x = Double
  11.      |     def get = 3.0
  12.      |   }
  13.      | }
  14. < console>:8: error: type mismatch;
  15.  found   : java.lang.Object with S
  16.  required: java.lang.Object with S{type x = Int}
  17.          override def newS = new S {

The fix for this example is to be explicit in the definition of the function in the superclass

Tuesday, April 27, 2010

Implicit Parameter Resolution

This topic is a continuation of the previous implicit parameter topics:

This topic provides some explanation about how implicit parameters are resulted. There are very strict rules for which implicit value is to be applied to a implicit parameter. A simple way to think about it is that the "closest" definition will be used. Local scope, enclosing class, parent class, companion object of the desired type.
  1. class X(val i:Int)
  2. class Y(val i:Int)
  3. object X {
  4.   implicit def xx = new X(1)
  5. }
  6. class Method {
  7.   def x(implicit x:X)=println(x.i)
  8.   def y(implicit y:Y)=println(y.i)
  9. }
  10. trait M { 
  11.   self : Method =>
  12.   implicit def x1 = new X(10)
  13.   implicit def y1 = new Y(100)
  14.   def testy = y
  15.   def testx = x
  16. }
  17. trait SM extends M {
  18.   self : Method =>
  19.   implicit def x2 = new X(20)
  20.   implicit def y2 = new Y(200)
  21.   
  22.   def testy2 = y  
  23. }
  24. // implicit resolved from companion object of X
  25. new Method().x
  26. // explicit applied so that value is used
  27. new Method().x(new X(3))
  28. // implicit resolved from companion object of X
  29. // NOT from M.  This is because the call site of x 
  30. // is not within M therefore does not use the implicits in M
  31. // for resolution.
  32. (new Method with M).x
  33. implicit def x = new X(30)
  34. // local scope overrides companion object implicit
  35. new Method().x
  36. // explicit applied so that value is used
  37. new Method().x(new X(3))
  38. // local scope overrides companion object implicit
  39. (new Method with M).x
  40. // testy is defined within M so the implicits within M
  41. (new Method with M).testy
  42. // testx is defined within M so the implicit within M
  43. // overrides the companion object implicit
  44. (new Method with M).testx
  45. // testy is within M (not SM) so the implicit within M
  46. // is used
  47. (new Method with SM).testy
  48. // testy2 is within SM so the implicit within SM 
  49. // overrides the implicit in M and the companion object
  50. (new Method with SM).testy2

Output:

1
3
1
30
3
30
100
10
100
200

Monday, April 26, 2010

Implicit Parameters

Evidently the topic of implicit parameters has not yet been correctly addressed. There have been several topic that refer to implicit parameters but none that directly discuss them. So before I continue with the topic of implicit parameter resolution I will discuss implicit parameters.

First, implicit parameters are not the same as implicit object conversions. Implicit parameters provide a way to allow parameters of a method to be "found". This is similar to default parameters at a glance but in fact is a different mechanism for finding the "default" value. It differs from implicit object conversion in that it is only a way for parameters for a method to be resolved. Implicit object conversion allows methods to appear to be called on one object when in fact that object is being converted behind the scenes to another type. (more or less)

An implicit parameter is a parameter to method or constructor that is marked as implicit. This means that if a parameter value is not supplied then the compiler will search for an "implicit" value defined within scope (according to resolution rules.) Implicit parameter resolution rules will be discussed soon.

Example:
  1. scala> def p(implicit i:Int) = print(i)
  2. p: (implicit i: Int)Unit
  3. // defining a val/var/def as implicit 
  4. // means that it will be considered during implicit resolution
  5. scala> implicit val v=2
  6. v: Int = 2
  7. // scope is searched for a implicit value to sue
  8. // v is found as marked implicit
  9. scala> p               
  10. 2
  11. // explicit declarations always overrides implicit values
  12. scala> p(1)
  13. 1

Implicit parameters are very nice for simplifying APIs. For example the collections use implicit parameters to supply CanBuildFrom objects for many of the collection methods. This is because normally the user does not need to be concerned with those parameters. Another example is supplying an encoding to an IO library so the encoding is defined once (perhaps in a package object) and all methods can use the same encoding without having to define it for every method call.

One important restriction is that there can only be a single implicit keyword per method. It must be at the start of a parameter list (which also makes all values of that parameter list be implicit). I further understand that only the last parameter list may be implicit.

Here are several illegal examples:
  1. // implicit is not in last parameter list
  2. scala> def pp(implicit i:Int, a:Int)(b:Int) = println(a,i)                 
  3. < console>:1: error: '=' expected but '(' found.
  4.        def pp(implicit i:Int, a:Int)(b:Int) = println(a,i)
  5. // there are 2 implicit parameters
  6. scala> def pp(implicit j:Int, a:Int)(implicit i:Int,b:Int) = println(a,i)
  7. < console>:1: error: '=' expected but '(' found.
  8.       def pp(implicit j:Int, a:Int)(implicit i:Int,b:Int) = println(a,i)
  9. // implicit is not the first parameter of the parameter list
  10. scala> def pp(a:Int, implicit i:Int) = println(i,j)         
  11. < console>:1: error: identifier expected but 'implicit' found.
  12.        def pp(a:Int, implicit i:Int) = println(i,j)
  13.                      ^

Here are several legal examples (Updated with useage examples):
  1. scala> implicit def v = 7
  2. v: Int
  3. scala> implicit var x = 10L
  4. x: Long
  5. // i is implicit
  6. scala> def pp(a:Int)(implicit i:Int) = println(a,i)
  7. pp: (a: Int)(implicit i: Int)Unit
  8. scala> pp(3)
  9. (3,7)
  10. // both i and b are implicit
  11. scala> def pp(a:Int)(implicit i:Int, b:Long) = println(a,i,b) 
  12. pp: (a: Int)(implicit i: Int,implicit b: Long)Unit
  13. scala> pp(4)               
  14. (4,7,10)
  15. // both i and b are implicit
  16. scala> def pp(implicit i:Int, b:Long) = println(i,b)  
  17. pp: (implicit i: Int,implicit b: Long)Unit
  18. scala> pp
  19. (7,10)
  20. // all or none of the parameters must be supplied
  21. scala> pp(2)
  22. < console>:13: error: not enough arguments for method pp: (implicit i: Int,implicit b: Long)Unit.
  23. Unspecified value parameter b.
  24.        pp(2)
  25. // This is syntactically legal but I cannot seem to implicitly invoke this
  26. // I would recommend: def pp(b:Long*)(implicit i:Int) = println(i,b)
  27. scala> def pp(implicit i:Int, b:Long*) = println(i,b)
  28. pp: (implicit i: Int,implicit b: Long*)Unit
  29. scala> pp(3,1,2,3)
  30. (3,WrappedArray(1, 2, 3))
  31. scala> def pp(b:Long*)(implicit i:Int) = println(i,b)
  32. pp: (b: Long*)(implicit i: Int)Unit
  33. scala> pp(1,2,3)
  34. (7,WrappedArray(1, 2, 3))

A related topic is Companion Object implicits.

Tuesday, April 20, 2010

Breaks

Scala 2.8 added the break control flow option. It is not implemented as a special language feature. Rather it is simply implemented as an object/trait using standard Scala mechanisms. If you are interested in creating a control flow object similar to this look at the Defining Custom Control Structures post.

The Break functionality is works basically how you would expect:
  1. // Import the control flow methodsmethods
  2. scala> import util.control.Breaks._
  3. import util.control.Breaks._
  4. // pass a function to the breakable method
  5. scala> breakable {
  6.      | for (i <- 1 to 10 ) {
  7.      | if(i > 5) break  // call break when done
  8.      | println(i)
  9.      | }
  10.      | }
  11. 1
  12. 2
  13. 3
  14. 4
  15. 5

Pretty intuitive but beware, break only breaks out to the first enclosing breakable. Here is an example of the issue:
  1. scala> def loop(f : Int => Boolean) = breakable {
  2.      | for (i <- 1 to 300) if (f(i)) break else println(i)
  3.      | }
  4. loop: (f: (Int) => Boolean)Unit
  5. // This never ends because break is caught by breakable in the loop method
  6. scala> breakable {
  7.      | while(true) {
  8.      | loop{ i => break; true }
  9.      | }
  10.      | }

Fortunately the implementers provide an elegant way to handle these sorts of cases. The Breaks object extends the Breaks class. By instantiating other instances of Breaks it is possible to control which breaks capture
  1. scala> import scala.util.control._
  2. import scala.util.control._
  3. scala> 
  4. scala> def loop(f : Int => Boolean) = {
  5.      |   val Inner = new Breaks
  6.      |   Inner.breakable {
  7.      |     for (i <- 1 to 4) if (f(i)) Inner.break else println(i)
  8.      |   }
  9.      | }
  10. loop: (f: (Int) => Boolean)Unit
  11. scala> 
  12. scala> val Outer = new Breaks
  13. Outer: scala.util.control.Breaks = scala.util.control.Breaks@1ba4806
  14. scala> Outer.breakable {
  15.      |   while(true) {
  16.      |     loop{ i => if(i==4) Outer.break; false}
  17.      |   }
  18.      | }
  19. 1
  20. 2
  21. 3

Tuesday, April 13, 2010

Creating Custom Traversable implementations

One of the most talked about features of Scala 2.8 is the improved Collections libraries. Creating your own implementation is trivial, however if you want your new collection to behave the same way as all the included libraries there are a few tips you need to be aware of.

Note: All of these examples can either be ran in the REPL or put in a file and ran

Starting with the simple implementation:
  1. import scala.collection._
  2. import scala.collection.generic._
  3. class MyColl[A](seq : A*) extends Traversable[A] {
  4.     // only abstract method in traversable is foreach... easy :) 
  5.   def foreach[U](f: A => U) = util.Random.shuffle(seq.toSeq).foreach(f)
  6. }

This is a silly collection I admit but it is custom :).

This example works but if you test the result of a map operation (or any other operation that returns a new instance of the collection) you will notice it is not an instance of MyColl. This is expected because unless otherwise defined Traversable will return a new instance of Traversable.

To demonstrate run the following tests:
  1. val c = new MyColl(1, 2, 3)
  2. println (c mkString ",")
  3. println(c mkString ",")
  4. println(c drop 1 mkString ",")
  5. // this two next assertions fail (see following explanation)
  6. assert(c.drop(1).isInstanceOf[MyColl[_]])
  7. assert((c map {_ + 1}).isInstanceOf[MyColl[_]])

Both assertions will fail. The reason for these failures is because the collection is immutable which dictates by necessity that a new object must be returned from filter/map/etc... Since the Traversable trait returns instances of Traversable these two assertions fail. The easiest way to make these methods return an instance of MyColl is to make the following changes/additions.
  1. import scala.collection._
  2. import scala.collection.generic._
  3. /*
  4. Adding GenericTraversableTemplate will delegate the creation of new
  5. collections to the companion object.  Adding the trait and
  6. companion object causes all the new collections to be instances of MyColl
  7. */
  8. class MyColl[A](seq : A*) extends Traversable[A] 
  9.                              with GenericTraversableTemplate[A, MyColl] {
  10.   override def companion = MyColl
  11.   def foreach[U](f: A => U) = util.Random.shuffle(seq.toSeq).foreach(f)
  12. }
  13. // The TraversableFactory trait is required by GenericTraversableTemplate
  14. object MyColl extends TraversableFactory[MyColl] {
  15. /* 
  16. If you look at the signatures of many methods in TraversableLike they have an
  17. implicit parameter canBuildFrom.  This allows one to define how the returned collections
  18. are built.  For example one could make a list's map method return a Set
  19. In this case we define the default canBuildFrom for MyColl
  20. */
  21.   implicit def canBuildFrom[A]: CanBuildFrom[Coll, A, MyColl[A]] = new GenericCanBuildFrom[A]
  22. /*  
  23. The method that builds the new collection.  This is a simple implementation
  24. but it works.  There are other implementations to assist with implementation if
  25. needed
  26. */
  27.   def newBuilder[A] = new scala.collection.mutable.LazyBuilder[A,MyColl[A]] {
  28.     def result = {
  29.       val data = parts.foldLeft(List[A]()){(l,n) => l ++ n}
  30.       new MyColl(data:_*)
  31.     }
  32.   }
  33. }

Now instances of MyColl will be created by the various filter/map/etc... methods and that is fine as long as the new object is not required at compile-time. But suppose we added a method to the class and want that accessible after applying methods like map and filter.

Adding val o : MyColl[Long] = c map {_.toLong} to the assertions will cause a compilation error since statically the class returned is Traversable[Long]. The fix is easy.

All that needs to be done is to add with TraversableLike[A, MyColl[A]] to MyColl and we are golden. There may be other methods as well but this works and is simple.

Note that the order in which the traits are mixed in is important. TraversableLike[A, MyColl[A]] must be mixed in after Traversable[A]. The reason is that we want methods like map and drop to return instances of MyColl (statically as well as dynamically). If the order was reversed then those methods would return Traversable event though statically the actual instances would still be MyColl.
  1. import scala.collection._
  2. import scala.collection.generic._
  3. class MyColl[A](seq : A*) extends Traversable[A]
  4.                              with GenericTraversableTemplate[A, MyColl] 
  5.                              with TraversableLike[A, MyColl[A]] {
  6.   override def companion = MyColl
  7.   def foreach[U](f: A => U) = util.Random.shuffle(seq.toSeq).foreach(f)
  8. }
  9. object MyColl extends TraversableFactory[MyColl] {  
  10.   implicit def canBuildFrom[A]: CanBuildFrom[Coll, A, MyColl[A]] = new GenericCanBuildFrom[A]
  11.   def newBuilder[A] = new scala.collection.mutable.LazyBuilder[A,MyColl[A]] {
  12.     def result = {
  13.       val data = parts.foldLeft(List[A]()){(l,n) => l ++ n}
  14.       new MyColl(data:_*)
  15.     }
  16.   }
  17. }

Now add in a new method to demonstrate that the new collection works as desired and we are done.

The following is the complete implementation with the tests. You can put it in a file and run scala <filename> or paste it into a REPL
  1. import scala.collection._
  2. import scala.collection.generic._
  3. import scala.collection.mutable.{ Builder, ListBuffer }
  4. class MyColl[A](seq : A*) extends Traversable[A]
  5.                              with GenericTraversableTemplate[A, MyColl] 
  6.                              with TraversableLike[A, MyColl[A]] {
  7.   override def companion = MyColl
  8.   def foreach[U](f: A => U) = util.Random.shuffle(seq.toSeq).foreach(f)
  9.   def sayhi = println("hi!")
  10. }
  11. object MyColl extends TraversableFactory[MyColl] {  
  12.   implicit def canBuildFrom[A]: CanBuildFrom[Coll, A, MyColl[A]] = new GenericCanBuildFrom[A]
  13.   def newBuilder[A] = new ListBuffer[A] mapResult (x => new MyColl(x:_*))
  14. }
  15. val c = new MyColl(1, 2, 3)
  16. println (c mkString ",")
  17. println(c mkString ",")
  18. assert(c.drop(1).isInstanceOf[MyColl[_]])
  19. assert((c map {_ + 1}).isInstanceOf[MyColl[_]])
  20. val o : MyColl[Int] = c filter {_ < 2}
  21. println(o mkString "," )
  22. o.sayhi

Friday, April 9, 2010

Elidable (remove method calls at compile time)

In Scala 2.8 there is a useful annotation called 'elidable'. This annotation flags a method so that given certain compiler flags all calls to the method will be removed. This is handy if you are writing a logger. The log methods can be annotated so that when compiling for production all log calls below a certain level would be removed from the compiled code. Several of the methods in Predef are annotated with elidable. Specifically, assume, assert and require.

The elidable annotation takes an Int parameter which specifies the priority of the method. The lower the integer the more likely the method would be removed during compilation. The elidable object defines several values that are used in our example.

When compiling with the -Xelide-below , the compiler parameter will remove all calls to elidable methods value and below.

To try the following example copy the example into a scala file (elidable.scala for example) and compile as indicated below:
  1. package example
  2. import scala.annotation.elidable
  3. import scala.annotation.elidable._
  4. object ElidableExamples {
  5.     @elidable(ALL) def all = println("all")
  6.     @elidable(ASSERTION) def assertion = println("assertion")
  7.     @elidable(CONFIG) def config = println("config")
  8.     @elidable(FINE) def fine = println("fine")
  9.     @elidable(FINER) def finer = println("finer")
  10.     @elidable(FINEST) def finest = println("finest")
  11.     @elidable(INFO) def info = println("info")
  12.     @elidable(OFF) def off = println("off")
  13.     @elidable(SEVERE) def severe = println("severe")
  14.     @elidable(WARNING) def warning = println("warning")
  15. }
  16. object Main extends Application {
  17. println("starting")
  18. import ElidableExamples._
  19. all
  20. assertion
  21. config
  22. fine
  23. finer
  24. finest
  25. info
  26. off
  27. severe
  28. warning
  29. println("ending")
  30. assert(false"boom!")
  31. }


Output from scalac elidable.scala && scala example.Main

starting
assertion
off
ending
java.lang.AssertionError: assertion failed: boom!
at scala.Predef$.assert(Predef.scala:93)
at example.Main$.(elidable.scala:34)
at example.Main$.(elidable.scala)
at example.Main.main(elidable.scala)

Output from scalac -Xelide-below 0 elidable.scala && scala example.Main

starting
assertion
config
fine
finer
finest
info
off
severe
warning
ending
java.lang.AssertionError: assertion failed: boom!
at scala.Predef$.assert(Predef.scala:93)
at example.Main$.(elidable.scala:34)
at example.Main$.(elidable.scala)
at example.Main.main(elidable.scala)

Output from scalac -Xelide-below 1000 elidable.scala && scala example.Main

starting
assertion
off
severe
ending
java.lang.AssertionError: assertion failed: boom!
at scala.Predef$.assert(Predef.scala:93)
at example.Main$.(elidable.scala:34)
at example.Main$.(elidable.scala)
at example.Main.main(elidable.scala)

Output from scalac -Xelide-below 3000 elidable.scala && scala example.Main

starting
off
ending

Tuesday, April 6, 2010

Variant Positions 3

The last few topics all discussed variance in its different forms. The following is a cheat sheet of the where the different variances exist within a class.

See Variant positions 1 for a discussion on one position in a class that is a contravariant position.

The example is:
  1. scala> class Output[+A] {def write(a : A) = () /*do write*/ }

In this example A in the method write is an contravariant position. Which means the previous definition is not legal because A is defined as Covariant in the class definition. In a class there are several positions with different variance characteristics. Here's the example from Programming in Scala:
  1. abstract class Cat[-T, +U] { 
  2.     def meow[W<sup>-</sup>](volume: T-, listener: Cat[U<sup>+</sup>, T<sup>-</sup>]-): Cat[Cat[U<sup>+</sup>, T<sup>-</sup>]-, U+]+
  3. }

If you remove the superscript + and - the above example actually compiles. The + and - indicate if the position is a covariant or contravariant position. Its not critical to memorize the positions (in my opinion). Just look it up as needed. The rule of thumb is that each nested position is inverted (flipped) value of it enclosing position.

That is all I will say about that :)

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:
  1. class Verified[+A <: V,V](assertion : (V) => Booleanprivate var value : A){
  2.     assert(assertion(value))
  3.     
  4.     def a = value
  5. // update is illegal.  See the example below
  6.     def update[ B >: A <: V](a : B) = value = a
  7. }
  8. def notNull(obj : AnyRef) = obj != null
  9. val v = new Verified(notNull, "hi")
  10. /*
  11. Up to this point everything looks ok but the next line
  12. will assign an object to value which is a reference to a String
  13. */
  14. 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:
  1. scala> class X[+A](val x :A)
  2. defined class X
  3. scala> class Y[A](var a: A) extends X[A](a)
  4. defined class Y
  5. scala> val x: X[Any] = new Y[String]("hi")
  6. x: X[Any] = Y@1732a4df
  7. scala> 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
  1. scala> def printList(l : List[Any]) = print(l mkString " :: ")
  2. printList: (l: List[Any])Unit
  3. scala> val buffer = Buffer(1,2,3)
  4. buffer: scala.collection.mutable.Buffer[Int] = ArrayBuffer(1, 2, 3)
  5. scala> printList(buffer)
  6. 1 :: 2 :: 3
  7. /*
  8. ++ is part of Iterable.  Since Iterable is covariant ++ 
  9. returns a new buffer it does not modify the existing buffer
  10. All mutators are only defined on invariant traits
  11. */
  12. scala> buffer ++ List(4)
  13. res16: scala.collection.mutable.Buffer[Int] = ArrayBuffer(1, 2, 3, 4)
  14. scala> res16 eq buffer
  15. res17: Boolean = false
  16. /*
  17. buffer defines += to add an element to the buffer
  18. so res27 is the same buffer with an extra element
  19. */
  20. scala> buffer += 10
  21. res27: buffer.type = ArrayBuffer(1, 2, 3, 10)
  22. scala> res27 eq buffer
  23. res28: 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:
  1. scala> class Output[+A] {def write(a : A) = () /*do write*/ }
  2. < console>:5: error: covariant type A occurs in contravariant position in type A of value a
  3.        class Output[+A] {def write(a : A) = () /*do write*/ }
  4.                                    ^

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.
  1. class Verified[+A] (assertion : (A) => Boolean, value : A){
  2.     assert(assertion(value))
  3.     
  4.     def a = value
  5.     def a_=(a : A) = new Verified(assertion, a)
  6. }

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:
  1. class Verified[+A <: V,V](assertion : (V) => Booleanval value : A){
  2.     assert(assertion(value))
  3. /*
  4. this is the key.  Restrict possible types of
  5. A Since B is a super (or equal) type of A
  6. */
  7.     def update[ B >: A <: V](a : B) = new Verified(assertion, a)
  8. }
  9. // example useage
  10. scala> def notNull(obj : AnyRef) = obj != null
  11. notNull: (obj: AnyRef)Boolean
  12. scala> val v = new Verified(notNull, "hi")
  13. v: Verified[java.lang.String,AnyRef] = Verified@307b37df
  14. scala> val newV = v update (new Object())
  15. newV: Verified[java.lang.Object,AnyRef] = Verified@36f72f09
  16. // 3 is not legal because the type parameter 'V' is AnyRef.  Int is a subclass of Any NOT AnyRef
  17. scala> val newV = v update (3)           
  18. < console>:8: error: inferred type arguments [Any] do not conform to method update's type parameter bounds [B >: java.lang.String <: AnyRef]
  19.        val newV = v update (3)
  20.                   ^

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:
  1. val l : List[Object] = List("this is legal")

Contravariance provides the opposite:
  1. // If the type parameter of list was contravariant this would be legal:
  2. val l : List[String] = List(new Object())

As covariance is indicated by a '+' before the type contravariance is indicated by a '-'
  1. scala> class X[-A]
  2. defined class X
  3. scala> val l : X[String] = new X[Object]
  4. 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)
  1. class Output[+A] {def write(a : A) = () /*do write*/ }
  2. def writeObject(out : Output[Object]) = out.write("hello")
  3. /*
  4. Uh oh you this only is for outputting lists not Objects 
  5. (certainly not the String that is actually written)
  6. Runtime error for sure!
  7. */
  8. 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:
  1. scala> class Output[+A] {def write(a : A) = () /*do write*/ }
  2. < console>:5: error: covariant type A occurs in contravariant position in type A of value a
  3.        class Output[+A] {def write(a : A) = () /*do write*/ }
  4.                                    ^

Consider the implications of making A contravariant?
  1. // The definition of object is now legal
  2. class Output[-A] {def write(a : A) = () /*do write*/ }
  3. // this is now a safe method definition since the parameter of Output must be a Object or a super class
  4. def writeObject(out : Output[Object]) = out.write("hello")
  5. // Now this is illegal as it should be
  6. scala> writeObject(new Output[List[String]])
  7. < console>:8: error: type mismatch;
  8.  found   : Output[List[String]]
  9.  required: Output[java.lang.Object]
  10.        writeObject(new Output[List[String]])
  11.        
  12. // this is legal... 
  13. 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.

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:
  1. scala> var i = 1
  2. i: Int = 1
  3. scala> i += 1
  4. scala> i
  5. res3: Int = 2

To illustrate this is not a special case for Int the next example defines several operations and demonstrates in place variable mutation.
  1. scala> case class MyClass(i:Int) {      
  2.      | def +(j:Int) = new MyClass(j + i)
  3.      | def -(j:Int) = new MyClass(i - j)
  4.      | def ^(j:Int) = MyClass(j)
  5.      | def +|(j:Int) = new MyClass(j + i / 3)
  6.      | }
  7. defined class MyClass
  8. scala> var c = MyClass(1)
  9. c: MyClass = MyClass(1)
  10. scala> c+=6
  11. scala> c
  12. res5: MyClass = MyClass(7)
  13. scala> c -= 2
  14. scala> c
  15. res7: MyClass = MyClass(5)
  16. scala> c ^= 10
  17. scala> c
  18. res23: MyClass = MyClass(10)
  19. scala> c +|= 5
  20. scala> c
  21. res25: MyClass = MyClass(8)

Here are several more examples using existing classes in Scala. They are all immutable examples.
  1. scala> var l = Set(1,2,3) 
  2. l: scala.collection.immutable.Set[Int] = Set(1, 2, 3)
  3. scala> l += 10
  4. scala> l
  5. res7: scala.collection.immutable.Set[Int] = Set(1, 2, 3, 10)
  6. scala> var seq = Seq(5,6,3)
  7. seq: Seq[Int] = List(5, 6, 3)
  8. scala> seq :+= 10
  9. scala> seq                 
  10. res9: Seq[Int] = List(5, 6, 3, 10)
  11. scala> seq +:= 10   
  12. scala> seq       
  13. res11: Seq[Int] = List(10, 5, 6, 3, 10)
  14. scala> var list = List(32)
  15. list: List[Int] = List(32)
  16. scala> list ::= 12
  17. scala> list
  18. res13: List[Int] = List(12, 32)

Note: assignment operators can also be defined as methods to mutate an object
  1. scala> case class MyClass(var i:Int) {
  2.      | def += (j:Int) = { i+=j ; this }
  3.      | }
  4. defined class MyClass
  5. scala> val m = MyClass(6)
  6. m: MyClass = MyClass(6)
  7. scala> m += 7
  8. res0: MyClass = MyClass(13)
  9. scala> m += 9
  10. res1: MyClass = MyClass(22)
  11. scala> res1 eq m
  12. res2: Boolean = true

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:

  1. scala> import org.geoscript.GeoScript._
  2. import org.geoscript.GeoScript._
  3. scala> import org.geoscript.geometry._
  4. import org.geoscript.geometry._
  5. scala> val line = LineString((10, 10), (20, 20), (30, 40))
  6. line: org.geoscript.geometry.LineString = LINESTRING (10 10, 20 20, 30 40)
  7. /*
  8. create a polygon by buffering the line (essentially expanding the line by 10 units that is degrees if not otherwise specified)
  9. */
  10. scala> val poly = line buffer 10
  11. poly: 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...
  12. // query the area of the polygon
  13. scala> poly.area
  14. res0: Double = 1041.9912814842407
  15. // get the centroids of the polygon and line
  16. scala> line.centroid
  17. res1: org.geoscript.geometry.Point = POINT (21.12574113277207 24.188611699158105)
  18. scala> poly.centroid
  19. res2: org.geoscript.geometry.Point = POINT (20.79088988611118 24.43096430943361)
  20. /*
  21. Obviously the polygon and line intersect since the polygon is a buffer of the line
  22. */
  23. scala> poly.intersects(line)
  24. res3: Boolean = true
  25. scala> val poly2 = Geometry.fromWKT("POLYGON ((10 10, 10 20, 20 20, 20 15, 10 10))")
  26. poly2: org.geoscript.geometry.Geometry = POLYGON ((10 10, 10 20, 20 20, 20 15, 10 10))
  27. // less trivial intersects operation
  28. scala> poly intersects poly2
  29. res3: Boolean = true
  30. // not make a new geometry from the intersection of the two geometries
  31. scala> val intersecting = poly intersection poly2
  32. intersecting: org.geoscript.geometry.Geometry = POLYGON ((10 10, 10 20, 20 20, 20 15, 10 10))
  33. scala> intersecting.area 
  34. res6: Double = 75.0
  35. scala> import org.geoscript.projection._
  36. import org.geoscript.projection._
  37. /*
  38. None of the previous geometries has a projection associated.  
  39. A new geometry can have one created with a projection by using the in(Projection) method
  40. */ 
  41. scala> val latLongPoly = poly2 in Projection("epsg:4326")
  42. latLongPoly: org.geoscript.geometry.Geometry = POLYGON ((10 10, 10 20, 20 20, 20 15, 10 10))
  43. // now reproject the latlong projection to a french projection 
  44. scala> latLongPoly in Projection("epsg:21781")
  45. 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))

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:
  1. 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.
  1. val v : SomeObject = 2

Example:
  1. // first lets define a class
  2. scala> case class Randomly(x : Int, y : Double)
  3. defined class Randomly
  4. // the normal conversion
  5. scala> implicit def intToRandomly(i : Int) = new Randomly(i,0.0)
  6. intToRandomly: (i: Int)Randomly
  7. /*
  8. now a tuple for the other conversion.
  9. Important:  The two conversions must have different names. At least that is the case in Scala 2.8
  10. */
  11. scala> implicit def tupleToRandomly(i : (IntDouble)) = new Randomly(i._1, i._2)
  12. tupleToRandomly: (i: (IntDouble))Randomly
  13.  
  14. scala> val r1 : Randomly = 4                                                     
  15. r1: Randomly = Randomly(4,0.0)
  16. scala> val r2 : Randomly = (4, 6.0)
  17. r2: Randomly = Randomly(4,6.0)
  18. /*
  19. Suppose you want to do
  20. val r : Randomly = (4,4)
  21. you might think to implicitly convert from in to double
  22. */
  23. scala> implicit def intToDouble(i : Int) = i.toDouble
  24. intToDouble: (i: Int)Double
  25. // implicit chaining is not permitted
  26. scala> val r3 : Randomly = (4, 6)                    
  27. < console>:10: error: type mismatch;
  28.  found   : (IntInt)
  29.  required: Randomly
  30.        val r3 : Randomly = (4, 6)
  31. // Here is the legal option
  32. scala> implicit def intTupleToRandomly(t: (Int,Int)) = new Randomly(t._1,t._2.toDouble) 
  33. intTupleToRandomly: (t: (IntInt))Randomly
  34. scala> val r3 : Randomly = (4, 6)                                                      
  35. r3: Randomly = Randomly(4,6.0)