Showing posts with label implicit. Show all posts
Showing posts with label implicit. Show all posts

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.

Wednesday, April 21, 2010

Companion Object Implicits

When a method requires an implicit there are several ways that the implicit is resolved. One way is to search for an implicit definition in the companion object of the required type. For example: def x(implicit m:MyClass) parameter m will search local scope, class hierarchy and the MyClass companion object for an implicit val or def. (More on implicit resolution later).

To demonstrate the method put the following code block into a file and run the script:
  1. class X(val i:Int) {
  2.   def add(implicit x:X)=println(x.i+i)
  3. }
  4. object X {
  5.   implicit def xx = new X(3)
  6. }
  7. // implicit is obtained from companion object of X
  8. new X(3).add
  9. val other = new {
  10.   def print(implicit x:X)=println(x.i)
  11. }
  12. // implicit is obtained from companion object of X
  13. other.print
  14. implicit def x = new X(32)
  15. // implicit is obtained local scope
  16. other.print

Running: scala impl.scala should produce:
6
3
32

Monday, April 19, 2010

Scala 2.8 Arrays are not Traversables

One performance/consistency change that has been make in Scala 2.8 is to make Scala Array always be a Java Array. This has some consequences which we will examine in this post. The biggest one is that Array is not a Scala Collection/Traversable. It is implicitly converted to one but it is not an instance of a Traversable. There are several reasons this was done. Probably the biggest is for performance. Because a Scala array is a Java array there is no overhead when using a Scala array.

Thanks to implicit type conversion all the normal collection methods are useable with an array. Even better, after running a method like map the result will again be a Java array. So the API is much more consistent.

An example illustrating that an Array is not a Traversable:
  1. // This does not compile (which is good) 
  2. // because Traversable[Int] can never be an array
  3. scala> def x(t:Traversable[Int]) = t match {
  4.      | case x : Array[Int] => true          
  5.      | }
  6. < console>:13: error: pattern type is incompatible with expected type;
  7.  found   : Array[Int]
  8.  required: Traversable[Int]
  9.        case x : Array[Int] => true
  10.                 ^
  11. < console>:13: error: type mismatch;
  12.  found   : Array[Int]
  13.  required: Traversable[Int]
  14.        case x : Array[Int] => true
  15.               ^

Another example:
  1. scala> def x(t:Traversable[Int]) = t.isInstanceOf[Array[_]]
  2. x: (t: Traversable[Int])Boolean
  3. /* this evaluates to false because Array is converted
  4.  * to WrappedArray because it has to be implicitly converted
  5.  * to a Traversable.  Since Array is not a Traversable the resulting 
  6.  * object is not an Array
  7.  */
  8. scala> x(Array(1,2,3))                                     
  9. res24: Boolean = false
  10. scala> def x(t:Traversable[Int]) = println(t)
  11. x: (t: Traversable[Int])Unit
  12. // This method call demonstrates the previous assertion
  13. scala> x(Array(1,2,3))                                            
  14. WrappedArray(1, 2, 3)

So suppose you want to be able to accept and use arrays and Traversables in a method but you want to be able to
check that the parameter is an Array. Why not match against WrappedArray. You probably can, but you may get performance improvements in some cases if you don't require wrapping the array.

For a more concrete example of why you may want to do this. In a Input/Output routine I wrote I would write the data one way if the input was an Array: stream.write(array). But if the input was a traversable then I would have to handle it differently. My particular issue was more complicated than that but that is the basic issue.

So the work around is to define a Generic parameter for the method:
  1. scala> def x[T <% Traversable[Int]](t:T) = t match { 
  2.      | case x : Array[Int] => true                                
  3.      | }
  4. x: [T](t: T)(implicit evidence$1: (T) => Traversable[Int])Boolean
  5. scala> x(Array(1,2,3))                               
  6. res27: Boolean = true

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)

Thursday, February 11, 2010

Selection with Implicit Methods

This tip is a cool tip based on Daniel's Use Implicits to Select Types post. That post goes into more detail.

The idea is that depending on a given parameter of type T a particular type of object is required. There are several ways to do this. One would be to use matching to match the type T and create the correct object. Most likely the biggest draw back to matching is caused by type erasure. The following solution gets around that issue.

Two very interesting points.
  1. Implicit methods do not require parameters. They can be selected based only on type parameters
  2. Implicit methods are not subject to type erasure
  1. // the base class we need
  2. scala> abstract class X[T] { def id :Unit }
  3. defined class X
  4. /*
  5. One of the types we need created.  It is the catch all case
  6. */
  7. scala> implicit def a[T] =new X[T] { def id =println("generic") }
  8. a: [T]X[T]
  9. /*
  10. A specific subclass for integers
  11. */
  12. scala> implicit def b =new X[Int] { def id =println("Int") }
  13. b: X[Int]
  14. /*
  15. One simple usage.  The most specific implicit will be used to 
  16. create the object to be passed to g.  
  17. */
  18. scala> def f[T](a :T)(implicit g :X[T]) = g.id
  19. f: [T](a: T)(implicit g: X[T])Unit
  20. scala> f(5)
  21. Int
  22. scala> f('c')
  23. generic
  24. /*
  25. This example demonstrates how erasure is not an issue because 
  26. the selection of the implicit is done at compile time so 
  27. the correct type is selected.  If a match was used instead 
  28. then a more complicated solution would be required
  29. */
  30. scala> def g[T](l:List[T])(implicit i:X[T]) = i.id          
  31. g: [T](l: List[T])(implicit i: X[T])Unit
  32. scala> g(List(1,2,3))
  33. Int
  34. scala> g(List("a",2,3))
  35. generic

Thursday, January 28, 2010

Overcoming Type Erasure in matching 1

Since Scala runs on the JVM (it also runs on .NET but we this tip is aimed at Scala on the JVM) much of the type information that is available at compile-time is lost during runtime. This means certain types of matching are not possible. For example it would be nice to be able to do the following:
  1. scala> val x : List[Any] = List(1.0,2.0,3.0)
  2. x: List[Any] = List(1, 2, 3)
  3. scala> x match { case l : List[Boolean] => l(0) }

If you run this code the list matches the list of ints which is incorrect (I have ran the following with -unchecked so it will print the warning about erasure):
  1. scala> val x : List[Any] = List(1.0,2.0,3.0)
  2. x: List[Any] = List(1, 2, 3)
  3. scala> x match { case l : List[Boolean] => l(0) }         
  4. < console>:6: warning: non variable type-argument Boolean in type pattern List[Boolean] is unchecked since it is eliminated by erasure
  5.        x match { case l : List[Boolean] => l(0) }
  6.                           ^
  7. java.lang.ClassCastException: java.lang.Double cannot be cast to java.lang.Boolean
  8. at scala.runtime.BoxesRunTime.unboxToBoolean(Unknown Source)
  9. at .< init>(< console>:6)

Another example is trying to match on structural types. Wouldn't it be nice to be able to do the following (We will solve this in a future post):
  1. scala> "a string" match {
  2.      | case l : {def length : Int} => l.length
  3.      | }
  4. < console>:6: warning: refinement AnyRef{def length: Int} in type pattern AnyRef{def length: Int} is unchecked since it is eliminated by erasure
  5.        case l : {def length : Int} => l.length
  6.                 ^
  7. res5: Int = 8

So lets see what we can do about this. My proposed solution is to create an class that can be used as an extractor when instantiated to do the check.

This is a fairly advanced tip so make sure you have read up on Matchers and Manifests:

The key parts of the next examples are the Def class and the function 'func'. 'func' is defined in the comments in the code block.

The Def class is the definition of what we want to match. Once we have the definition we can use it as an Extractor to match List[Int] instances.

The Def solution is quite generic so it will satisfy may cases where you might want to do matching but erasure is getting in the way.

The secret is to use manifests:
  • When the class is created the manifest for the class we want is captured.
  • Each time a match is attempted the manifest of the class being matched is captured
  • In the unapply method, the two manifests are compared and when they match we can return the matched element but cast to the desired type for the compiler

It is critical to notice the use of the typeArguments of the manifest. This returns a list of the manifests of each typeArgument. You cannot simply compare desired == m because manifest comparisons are not deep. There is a weakness in this code in that it only handles generics that are 1 level deep. For example:

List[Int] can be matched with the following code but List[List[Int]] will match anything that is List[List[_]]. Making the method more generic is an exercise left to the reader.
  1. scala> import reflect._ 
  2. import reflect._
  3. /*
  4. This is the key class
  5. */
  6. scala> class Def[C](implicit desired : Manifest[C]) {
  7.      | def unapply[X](c : X)(implicit m : Manifest[X]) : Option[C] = {
  8.      |   def sameArgs = desired.typeArguments.zip(m.typeArguments).forall {case (desired,actual) => desired >:> actual}
  9.      |   if (desired >:> m && sameArgs) Some(c.asInstanceOf[C])
  10.      |   else None
  11.      | }
  12.      | }
  13. defined class Def
  14. // first define what we want to match
  15. scala> val IntList = new Def[List[Int]]
  16. IntList: Def[List[Int]] = Def@6997f7f4
  17. /*
  18. Now use the object as an extractor.  the variable l will be a typesafe List[Int]
  19. */
  20. scala> List(1,2,3) match { case IntList(l) => l(1) : Int ; case _ => -1 }
  21. res36: Int = 2
  22. scala> List(1.0,2,3) match { case IntList(l) => l(1) : Int ; case _ => -1 }
  23. res37: Int = -1
  24. // 32 is not a list so it will fail to match
  25. scala> 32 match { case IntList(l) => l(1) : Int ; case _ => -1 }
  26. res2: Int = -1
  27. scala> Map(1 -> 3) match { case IntList(l) => l(1) : Int ; case _ => -1 }
  28. res3: Int = -1
  29. /*
  30. The Def class can be used with any Generic type it is not restricted to collections
  31. */
  32. scala> val IntIntFunction = new Def[Function1[Int,Int]]
  33. IntIntFunction: Def[(Int) => Int] = Def@5675e2b4
  34. // this will match because it is a function
  35. scala> ((i:Int) => 10) match { case IntIntFunction(f) => f(3); case _ => -1}
  36. res38: Int = 10
  37. // no match because 32 is not a function
  38. scala> 32 match { case IntIntFunction(f) => f(3); case _ => -1}
  39. res39: Int = -1
  40. // Cool Map is a function so it will match
  41. scala> Map(3 -> 100) match { case IntIntFunction(f) => f(3); case _ => -1}
  42. res6: Int = 100
  43. /*
  44. Now we see both the power and the limitations of this solution.
  45. One might expect that:
  46.    def func(a:Any) = {...} 
  47. would work.  
  48. However, if the function is defined with 'Any' the compiler does not pass in the required information so the manifest that will be created will be a Any manifest object.  Because of this the more convoluted method declaration must be used so the type information is passed in. 
  49. I will discuss implicit parameters in some other post
  50. */
  51. scala> def func[A](a:A)(implicit m:Manifest[A]) = {
  52.      |   a match {
  53.      |     case IntList(l) => l.head                   
  54.      |     case IntIntFunction(f) => f(32)             
  55.      |     case i:Int => i                             
  56.      |     case _ => -1                                
  57.      |   } 
  58.      | }
  59. func: [A](a: A)(implicit m: Manifest[A])Int
  60. scala> func(List(1,2,3))                           
  61. res16: Int = 1
  62. scala> func('i')                                   
  63. res17: Int = -1
  64. scala> func(4)
  65. res18: Int = 4
  66. scala> func((i:Int) => i+2)
  67. res19: Int = 34
  68. scala> func(Map(32 -> 2))
  69. res20: Int = 2

Sunday, November 22, 2009

Type aliasing and implicit conversions in harmony

This topic combines advanced two topics. types and implicit methods. This topic is not really intended to be instructional as much as a illustration of the cool stuff you can do with the language. As always becareful not to cut yourself :-)

In keeping with the uniform principle of Scala, types can defined as variables. This is useful for several reasons but this topic covers a cool/fun effect of having types be declared like variables. Type Aliasing. In the example below an alias | is created for the Either class. This allows the | to be used in place of Either.
  1. // Start of example 1.  creating the type alias
  2. // define the | type
  3. scala> type |[A,B] = Either[A,B]
  4. defined type alias $bar
  5. // If a Type has 2 parameters it can be used in operator form
  6. scala> Array[Int | Long](Left(5),Right(12L))
  7. res0: Array[|[Int,Long]] = Array(Left(5), Right(12))
  8. // start of example 2.  Simplify creation of an Array (or other collection) of either
  9. scala> implicit def makeLeft[A,B](a:A):Either[A,B] = Left(a)
  10. makeLeft: [A,B](a: A)Either[A,B]
  11. scala> implicit def makeRight[A,B](b:B):Either[A,B] = Right(b) 
  12. makeRight: [A,B](b: B)Either[A,B]
  13. // Since there are implicits to convert to Right and Left the values in the Array will be wrapped
  14. // automatically.  Makes the syntax MUCH cleaner and easier to read.
  15. scala> Array[Int|String](5,"Wurst",123,2,6)
  16. res1: Array[|[Int,String]] = Array(Left(5), Right(Wurst), Left(123), Left(2), Left(6))

Wednesday, November 18, 2009

Manifests

Added in Scala 2.7 as an experimental feature is the manifest class it is a handy class allowing for very limited type reification. Take the following example:
def inspect[T](l:List[T])
If you want to know they type of T in Java you are out of luck because that type information is gone. However, Scala offers help through manifests.
  1. def inspect[T](l:List[T])(implicit manifest : scala.reflect.Manifest[T]) = println(manifest.toString)

This code snippet will print out the string representation of type T.

More Examples:
  1. scala> def inspect[T](l:List[T])(implicit manifest : scala.reflect.Manifest[T]) = println(manifest.toString)
  2. inspect: [T](List[T])(implicit scala.reflect.Manifest[T])Unit
  3. scala> inspect(List(1,2,3,4))
  4. int
  5. scala> inspect(List(List(1,2),List(3,4)))
  6. scala.List[int]
  7. scala> inspect(List(List(List(1),List(2)),List(List(3),List(4))))
  8. scala.List[scala.List[int]]
  9. scala> val l:List[Iterable[Int]] = List(List(1,2))  
  10. l: List[Iterable[Int]] = List(List(1, 2))
  11. scala> inspect(l)                                                                                           
  12. scala.collection.Iterable[Int]
  13. scala> class MV[T](val v:T)(implicit m:scala.reflect.Manifest[T]) { println(m.toString) }
  14. defined class MV
  15. scala> new MV(1)
  16. Int
  17. res1: MV[Int] = MV@180e6899
  18. scala> class MV2[T](val v:T)(implicit m:scala.reflect.Manifest[T]) {                     
  19.      | def isA[A](implicit testManifest:scala.reflect.Manifest[A]) = m.toString == testManifest.toString
  20.      | }
  21. defined class MV2
  22. scala> val x = new MV2(19)
  23. x: MV2[Int] = MV2@41ff8506
  24. scala> x.isA[String]
  25. res2: Boolean = false
  26. scala> x.isA[Int]   
  27. res3: Boolean = true
  28. scala> def isA[A](o:Object)(implicit m:Manifest[A]) = {
  29.      | val `class` = Class.forName(m.toString)         
  30.      | `class`.isAssignableFrom(o.getClass)            
  31.      | }
  32. isA: [A](o: java.lang.Object)(implicit m: scala.reflect.Manifest[A])Boolean
  33. scala> isA[java.lang.Integer](java.lang.Integer.valueOf(19))
  34. res6: Boolean = true

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)

Monday, August 31, 2009

Java Conversions

The collections APIs in Scala are completely separate from those in Java, the primary reason is because they are designed from the ground up to be functional in nature. However, in keeping with Scala's goal of effortless Java integration there are implicits that convert between Java and Scala collections.

The object that needs to be imported has changed between Scala 2.7.x and 2.8 but the usage is the same. In 2.8 all conversions from Java to Scala objects (be it collections, concurrent constructs or others) are in objects call JavaConversions. For example in the August 31 nightly build there are 2 JavaConversions objects. scala.collection.JavaConversions and scala.concurrent.JavaConversions. As time progresses I expect that the types of JavaConversions will expand.

In scala 2.7 you can not convert Scala collections to Java collections only Java to Scala. In 2.8 you can do both.

So here is how to use conversions in 2.7.x:
  1. scala>val jmap = new java.util.HashMap[String,Int]()
  2. jmap: java.util.HashMap[String,Int] = {}
  3. scala> jmap.put("one", 2)
  4. res0: Int = 0
  5. scala> jmap.get("one") 
  6. res1: Int = 2
  7. scala>import scala.collection.jcl.Conversions._
  8. import scala.collection.jcl.Conversions._
  9. scala> jmap("one")
  10. res3: Int = 2
  11. scala> jmap("one") = 1
  12. scala> jmap("one")
  13. res5: Int = 1


And in 2.8:
  1. scala>val jmap = new java.util.HashMap[String,Int]()
  2. jmap: java.util.HashMap[String,Int] = {}
  3. scala> jmap.put("one", 2)
  4. res0: Int = 0
  5. scala> jmap.get("one")
  6. res1: Int = 2
  7. scala>import scala.collection.JavaConversions._
  8. import scala.collection.JavaConversions._
  9. scala> jmap("one")
  10. res3: Int = 2
  11. scala> jmap("one") = 1
  12. scala> jmap("one")
  13. res5: Int = 1

Wednesday, August 19, 2009

Implicit Methods

Scala is designed to be a scalable language and one if the features that assist in realizing this goal are implicit methods. I have seen it occasionally referred to as static duck typing (although I personally think that describes structural typing better).

The idea is to be able to extend an existing class with new methods in a type safe manner. Essentially what happens is a developer defines an implicit method (or imports an implicit method) which converts one object from one type to another type. Once the implicit method is within scope all methods are available from both types.

Note: The conversion is one way. If you want bi-directional conversion you need 2 methods.
  1. scala>class MyInteger( i:Int ) {
  2.      | def myNewMethod = println("hello from myNewMethod")
  3.      | }
  4. defined class MyInteger
  5. // an integer is not a "MyInteger" so you cannot call the myNewMethod on it
  6. scala> 1.myNewMethod
  7. :5: error: value myNewMethod is not a member of Int
  8.        1.myNewMethod
  9.          ^
  10. // define an implicit conversion and now we can call all MyInteger methods on integers
  11. scala> implicit def int2MyInt( i:Int ) = new MyInteger(i)
  12. int2MyInt: (Int)MyInteger
  13. scala> 1.myNewMethod
  14. hello from myNewMethod
  15. scala>class MyInteger2( val i:Int ) {
  16.      | def inc = new MyInteger2( i+1 )
  17.      | }
  18. defined class MyInteger2
  19. // you can define several implicits in an object and import later
  20. scala>object Conversions{
  21.      | implicit def int2MyInt2(i:Int) = new MyInteger2(i)
  22.      | implicit def myInt2Int( mI:MyInteger2) = mI.i
  23.      | }
  24. defined module Conversions
  25. // you can import all implicits    or just    one (although interpreter only allows all for some reason)
  26. scala>import Conversions._
  27. import Conversions._
  28. // inc returns a MyInteger2 instance
  29. scala>val j = 1.inc
  30. j: MyInteger2 = MyInteger2@37f808e6
  31. // test    the bi-directionality of it all    so create a method
  32. // that takes and int
  33. scala>def takesInt( i:Int ) = println(i)
  34. takesInt: (Int)Unit
  35. // j is a MyInteger2 but thanks to the implicit it can be converted back to an int
  36. scala> takesInt( j )
  37. 2
  38. scala> takesInt ( 5.inc )
  39. 6