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

1 comment: