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

4 comments:

  1. I think the statement that there's an implicit "+=" is misleading. Scala will convert "x += 5" into "x = x + 5". It will not automatically add a "+=" method to it, however. I think the distinction is important.

    ReplyDelete
  2. A fair point. I have updated the post so it is (hopefully) more accurate

    ReplyDelete
  3. What's the difference between
    def -(j:Int) = new MyClass(i - j)
    and
    def ^(j:Int) = MyClass(j)
    that makes the second one not need the "new" ? This is something I keep seeing in Scala and not understanding.

    ReplyDelete
  4. Hi Andrew.

    This is explained in http://daily-scala.blogspot.com/2009/09/companion-object.html

    all case classes have a factory method in a like named object. So you can leave out the 'new'

    consider:

    object File {
    def apply(param:String) = new java.io.File(param)
    }

    This declaration allows you to create a file via:

    File("/tmp/")

    All case classes have a method like this created. And as you can see you can also create them.

    ReplyDelete