Showing posts with label assignment. Show all posts
Showing posts with label assignment. Show all posts

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

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:
  1. def x+ = 3   // not valid
  2. def x_+ = 3  // valid
  3. def +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)

  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.  
  8.  scala> val c = MyClass(3)
  9.  c: MyClass = MyClass(3)
  10.  scala> c + 4
  11.  res26: MyClass = MyClass(7)
  12.  scala> c-2 
  13.  res27: MyClass = MyClass(1)
  14.  scala> c -6
  15.  res28: MyClass = MyClass(-3)
  16.  scala> c ^ 3
  17.  res29: MyClass = MyClass(3)
  18.  scala> c+|5
  19.  res31: MyClass = MyClass(6)

Monday, February 1, 2010

Temporary variables during object instantiation

In Java a common pattern with class constructors is to assign field values and often there are several intermediate values used for the calculation. If the code is ported to Scala the resulting class will have the intermediate values as fields, which take up space in the object. However the issue is easily worked around. Lets look at a couple examples.

Example 1: Assigning a single field
  1. //  Java
  2. import java.io.File
  3. /** 
  4. No real logic behind class.  But for some reason it needs the path of a tmp directory in the working directory
  5. */
  6. class OneAssignment {
  7.   final String field;
  8.   public OneAssignment() {
  9.     File file = new File("tmp");
  10.     if(!file.exists()) {
  11.       file.mkdirs();
  12.     }
  13.     field = file.getAbsolutePath();
  14.   }
  15. }

In Scala the naive way to port this would be:
  1. //  Scala
  2. import java.io.File
  3. class OneAssignment {
  4.   val file = new File("tmp")
  5.   if(!file.exists()) {
  6.     file.mkdirs()
  7.   }
  8.   val field = file.getAbsolutePath()
  9. }

Problem is that it has an extra field "file" now. The correct way to port this would be as follows:
  1. //  Scala
  2. import java.io.File
  3. class OneAssignment {
  4. /* 
  5. notice that assignment is in a block so file is only visible within the block
  6. */
  7.   val field = {
  8.     val file = new File("tmp")
  9.     if(!file.exists()) {
  10.       file.mkdirs()
  11.     }
  12.     file.getAbsolutePath()
  13.   }
  14. }


Example 2: Assigning multiple fields
  1. //  Java
  2. import java.io.File
  3. /** 
  4. Basically the same as last example but multiple fields are assigned
  5. Notice that 2 fields depend on the temporary file variable but count does not
  6. */
  7. class MultipleAssignments {
  8.   final String tmp,mvn_repo;
  9.   find int count;
  10.   public OneAssignment() {
  11.     File file = new File("tmp");
  12.     if(!file.exists()) {
  13.       file.mkdirs();
  14.     }
  15.     tmp = file.getAbsolutePath();
  16.     count = file.listFiles.length;
  17.     
  18.     File home = new File(System.getProperty("user.home"));
  19.     mvn_repo = new File(home, ".m2").getPath();
  20.   }
  21. }

The Scala port:
  1. //  Scala
  2. import java.io.File
  3. class MultipleAssignments {
  4. /*
  5. When multiple fields depend on the same temporary variables the fields can be assigned together from one block by returning a tuple and using Scala's matching to expand the tuple during assignment.  See previous topics on assignment for details 
  6. */
  7.   val (tmp,count) = {
  8.     val file = new File("tmp");
  9.     if(!file.exists()) {
  10.       file.mkdirs();
  11.     }
  12.     val tmp = file.getAbsolutePath();
  13.     val count = file.listFiles.length;
  14.     (tmp, count)
  15.   }
  16.   val mvn_repo = {
  17.     val home = new File(System.getProperty("user.home"));
  18.     new File(home, ".m2").getPath();
  19.   }
  20. }

In some ways the Scala port is cleaner in that it splits the constructor up and decouples the dependencies between fields.

Thursday, November 12, 2009

Import Instance Properties

A truly fantastic aspect of Scala is the uniform principle that Scala attempts to adhere to. In other words Scala tries to not make any rules that only apply to a single case when it can be applied generally.

One example is matching you can see several uses of matching in the following topics:
But matching it applies to today's topic as well. This topic covers a cool trick that helps assist with parameter objects and complex return types.

This topic is another take on Assignment and Parameter Objects. There are cases when a method has a large number of parameters and the API can be cleaned up by introducing a parameter object. Or perhaps an object with several public proprties are passed to a method.
  1. scala> case class Params(p1:Int, p2:Int, p3:Int)
  2. defined class Params
  3. scala>  def method(params:Params) = {
  4.      |   println(params.p1, params.p2, params.p3)
  5.      | }
  6. method: (Params)Unit
  7. scala> method(Params(1,2,3))
  8. (1,2,3)
  9. }

The symbol 'params' introduces noise into the code. The noise can be reduced further by assigned the properties of the parameter object to local variables:
  1. scala>  case class Params(p1:Int, p2:Int, p3:Int)
  2. defined class Params
  3. scala> 
  4. scala>  def method(params:Params) = {
  5.      |   val Params(p1,p2,p3) = params
  6.      |   println(p1,p2,p3)
  7.      | }
  8. method: (Params)Unit
  9. scala> method(Params(1,2,3))
  10. (1,2,3)
  11. }

But we can do better remember that we can import methods and properties from an object:
  1. scala> object Obj {
  2.      |  val prop = 10
  3.      | }
  4. defined module Obj
  5. scala> import Obj._
  6. import Obj._
  7. scala> println(prop)
  8. 10

Since all instance are objects it is possible to import fields and methods from instances as well:
  1. scala>  case class Params(p1:Int, p2:Int, p3:Int)
  2. defined class Params
  3. scala> 
  4. scala>  def method(params:Params) = {
  5.      |   import params._
  6.      |   println(p1, p2, p3)
  7.      | }
  8. method: (Params)Unit
  9. }

The same technique is extremely useful when a method needs to return multiple values:
  1. scala>  def method() = {
  2.      |   (1,2,3)
  3.      | }
  4. method: ()(IntIntInt)
  5. scala> val retVal = method()
  6. retVal: (IntIntInt) = (1,2,3)
  7. /*
  8.  retVal is a tuple so we can import the tuple
  9.  properties.  Becareful to not do this multiple times in
  10.  the same scope
  11. */
  12. scala> import retVal._
  13. import retVal._
  14. scala> println(_1,_2,_3)
  15. (1,2,3)
  16. scala> def method2={
  17.        // Notice case class declaration can be contained in method
  18.      | case class Return(v1:Int,v2:Int)
  19.      | Return(6,7)
  20.      | }
  21. method2: java.lang.Object with ScalaObject with Product{def v1: Intdef v2: Int}
  22. scala> val r = method2
  23. r: java.lang.Object with ScalaObject with Product{def v1: Intdef v2: Int} = Return(6,7)
  24. scala> import r._
  25. import r._
  26. scala> println(v1,v2)
  27. (6,7)
  28. }

Thursday, September 10, 2009

Assignments

Assignment in Scala follows more or less the same rules as Java and other related rules. There are some differences.

Increment operators do not work (i++). As I understand it the rational is that it is too specific an idiom. There is not an easy way to generalize it. Instead you must use i += 1.

Assignments do not return a value. For example you cannot do val i = j = 2 or while( (i = read(buffer)) > 0 ){...}.

One feature that is fairly unique in Scala is the ability to expand a case-class or other class that has an associated extractor. For details look at the previous topic Assignment and Parameter Objects.

Examples:

  1. scala> val i,j=2
  2. i: Int = 2
  3. j: Int = 2
  4. scala> val (i,j) = (1,2)
  5. i: Int = 1
  6. j: Int = 2
  7. scala> val (i,j,k) = (1,"two",3.0)
  8. i: Int = 1
  9. j: java.lang.String = two
  10. k: Double = 3.0
  11. scala> caseclass Data( name:String, age:Int, weight:Float)
  12. defined class Data
  13. scala> val Data(name, age, weight) = Data("Jesse", 133, 100f)
  14. name: String = Jesse
  15. age: Int = 133
  16. weight: Float = 100.0
  17. scala> val value = 1
  18. value: Int = 1
  19. scala> i += 1
  20. :10: error: reassignment to val
  21.        i += 1
  22.          ^
  23. scala> var variable = 1
  24. variable: Int = 1
  25. scala> variable += 1
  26. scala> println(variable)
  27. 2

Sunday, August 16, 2009

Assignment and Parameter Objects

One of the principle design goals of Scala is to be "deep" not wide, which means the language attempts to have a small set of rules that can be applied in many different ways in different situations. Pattern matching is one of my favourite examples of this. Pattern matching is commonly seen in match { case... } statements but you will also see it frequently in exception handling, function declaration and, most important for this post, assignment.

Scala does not have multiple assignment like some languages. Instead it has tuples and matching. Tuples are a light-weight way of grouping data in a simple data object. (1,2.0,'c',"string", true). A simple example of a 5 element tuple. Tuples can be up to 22 elements long and can be homogenous or heterogenous. Using this for multiple assignement works something like:
  1. val (firstname, lastname) = ("Jesse","Eichar")

This is pattern matching.
  1. scala> ("Jesse","Eichar") match {
  2.      | case (firstname,lastname) => println(firstname,lastname)
  3.      | }
  4. (Jesse,Eichar)

Notice that in both cases you need the brackets around firstname, lastname. This instructs the compiler that you are matching against a Tuple.

Now the interesting use is with parameter objects. Tuples are a poor substitute for parameter objects because they do not have context. Changing:
  1. def myMethod( firstname:String, middlename:String, lastname:String) = {...}

to
  1. def myMethod( name:(String,String,String)) = {...}

Is not a good change because you loose context. What are the 3 strings? The information must go in the Javadocs. A better option:
  1. caseclass Name(first:String, middle:String, last:String)
  2. def myMethod( name:Name ) = {
  3.   val Name(first, middle, last) = name
  4.   // do something with first middle last
  5. }

The beauty is that you have an object that you can pass around easily. It is a case class therefore extracting the information is incredibly easy and unlike a tuple it has context and can have methods added easily.

Yes it is longer to write but if you need to reuse the data in several locations the trade off is well worth it in clarity.

Examples:
  1. // define name data object.
  2. // Notice toString is a lazy val.  This means value is calculated only once.
  3. scala>caseclass Name(first:String, middle:String, last:String) {
  4.      | override lazy val toString="%s, %s %s" format (last, first,middle)
  5.      | }
  6. defined class Name
  7. // toString formats name nicely.
  8. scala> Name("Jesse","Dale","Eichar")
  9. res1: Name = Eichar, Jesse Dale
  10. scala>def readName() = {
  11.      | //maybe load from a file or database
  12.      | Name("Jesse","Dale","Eichar") :: Name("Jody","","Garnett") :: Name("Andrea","","Antonello"):: Nil
  13.      | }
  14. readName: ()List[Name]
  15. scala>def firstLastName(name:Name) = {
  16.      |  // we are putting _ because we don't care about middle name
  17.      | val Name( first, _, last ) = name
  18.      | (first, last)
  19.      | }
  20. firstLastName: (Name)(String, String)
  21. // convert the list of Names to a list of tuples of first and last name
  22. scala> readName().map( firstLastName _ )
  23. res2: List[(String, String)] = List((Jesse,Eichar), (Jody,Garnett), (Andrea,Antonello))
  24. // print all first names starting with J
  25. scala>for( Name(first,_,_) <- readName; if (first.startsWith("J"))) println(first)
  26. Jesse
  27. Jody
  28. // print the first and middle parts of the first name in the list
  29. scala> readName() match {
  30.      | // the :: _ indicates that we are matching against a list but we don't care about the rest of the list
  31.      | case Name(f,m,_) ::  _ => println( f, m)
  32.      | case _ => println("Not a list of names")
  33.      | }
  34. (Jesse,Dale)

Thursday, August 6, 2009

Simple classes

Scala classes are similar to java classes but are designed to work around some of the boiler plate code. We will address multiple constructors another day. But one thing to note is that normally the code that is in a Java constructor goes directly in the body of a Scala class or in a companion object (also covered later).
  1. scala>class NewClass( aPrivateInt:Int, val iAmPublic:Int, var iAmAlsoPublic:Int)
  2. defined class NewClass
  3. scala>val c = new NewClass(1,2,3)
  4. c: NewClass = NewClass@da99836
  5. scala> c.aPrivateInt
  6. :7: error: value aPrivateInt is not a member of NewClass
  7.        c.aPrivateInt
  8.          ^
  9. scala> c.iAmPublic
  10. res2: Int = 2
  11. scala> c.iAmAlsoPublic
  12. res3: Int = 3
  13. scala> c.iAmPublic = 10 // not allowed because iAmPublic is a val
  14. :6: error: reassignment to val
  15.        c.iAmPublic = 10
  16.                    ^
  17. scala> c.iAmAlsoPublic = 10 // iAmAlsoPublic is a var
  18. scala> c.iAmAlsoPublic
  19. res4: Int = 10
  20. scala>class c2( aPrivate:Int ) {
  21.      | val aPublic = aPrivate + 10
  22.      | }
  23. defined class c2
  24. scala>class c3(tmp:Int) extends c2(tmp){
  25.      | overrideval aPublic = tmp * 10 // can't access super.aPrivate because it
  26.  is classprivate so use tmp
  27.      | }
  28. defined class c3
  29. scala>new c2(10).aPublic
  30. res5: Int = 20
  31. scala>new c3(10).aPublic
  32. res6: Int = 100
  33. scala>class c4( aPrivate:Int ) {
  34.      | println(aPrivate)
  35.      | privatevalprivate2 = aPrivate % 10
  36.      | println(private2)
  37.      | }
  38. defined class c4
  39. scala>new c4(22)
  40. 22
  41. 2
  42. res7: c4 = c4@64d90254