In my previous article, I explained how to get started with Scala. I presented a brief overview of the Scala IDE and explained a few features of Scala for developers experienced with C#. In this article, I focus on the way Scala works with immutable values and mutable variables, classes and constructors, and the use of operators as method names.
Mutable Variables and Immutable Values
One of the most important concepts in functional programming is that operations of a program should map input values to output values instead of changing data in place. Thus, functional programming relies on immutable data structures and methods that should not have side effects. Because of this approach, functional programming principles increased in popularity when the need to run code in multiple threads became essential to translating multicore power into application performance. When methods work with immutable data items and don't generate side effects, the methods are inherently thread-safe.
An immutable object cannot change its state after construction, and an immutable reference cannot change its referred-to object after it has been assigned an initial reference. The String
type is a great example of an immutable object in C#. Each time you call a method to modify a String
, the method returns a new String
instance whose value is the result of the requested operation performed on the original String
. For example, when you call the ToUpper
method for a String
instance, the method returns a new String
instance with all the characters converted to uppercase. The conversion to uppercase does not change the original String
.
Sometimes, it is more convenient to use mutable types in certain algorithms, instead of working with a pure functional style that relies on immutable types. However, as you make the move to Scala, you should take advantage of immutable types as much as possible in order to reduce side effects and allow your code to scale. This is part of the mindset change that accompanies moving to a new language, rather than just coding the same way but with a different syntax. When your code works with immutable types, it can easily run it in parallel without having to worry about the problems of multiple concurrent pieces of code making changes to shared variables.
To understand how Scala offers you the ability to work with mutable and immutable variables, let's use the Scala interpreter. Launch it by selecting Window | Show View | Scala Interpreter in the main menu of the Scala IDE.
Enter the following Scala expression in the Evaluate
textbox:
var mutableLabel = "I'm a mutable string"
The Scala interpreter (also known as REPL) will display the following line:
mutableLabel: String = I'm a mutable string
The type-inference mechanism determined that the best type is a String
, and provides the following result for the evaluated expression:
- variable name:
mutableLabel
- type:
String
- Assigned value:
I'm a mutable string
Because you've used the var
keyword to define the variable, the Scala interpreter has created a mutable String
, and now you can change the value assigned to the mutableLabel
variable to a new String
without limitations.
So, you can easily assign a new String
to the previously defined mutableLabel
mutable variable:
mutableLabel = "Now, I'm a new and different string. I told you I was a mutable string :)"
The Scala interpreter will display the following line with the results of evaluating the previous expression (see Figure 1):
mutableLabel: String = Now, I'm a new and different string. I told you I was a mutable string :)
Figure 1: The Scala interpreter with the results of defining a mutable string and changing its value.
Now, enter the following Scala expression in the Evaluate
textbox:
val immutableLabel = "I'm an immutable string. Nobody will change me!"
The Scala interpreter will display the following line:
immutableLabel: String = I'm an immutable string. Nobody will change me!
The type inference mechanism determined that the best type is a String
, and provides the following result for the evaluated expression:
- variable name:
immutableLabel
- type:
String
- Assigned value:
I'm an immutable string. Nobody will change me!
Because, in this case, you've used the val
keyword to define the variable, the Scala interpreter has created an immutable String
; thus, you cannot change the value assigned to the immutableLabel
variable. However, notice that the Scala interpreter doesn't discriminate between the mutable and immutable types, it just provides you the type name.
If you try to assign a new String
to the previously defined immutableLabel
immutable variable, the Scala interpreter will display the following error:
<console>:8: error: reassignment to val immutableLabel = "I think I cannot change immutableLable, can I?" ^
In sum, the val
keyword which defines an immutable value, while var
defines a regular, mutable variable.
Creating Classes
When you want to enter multiple lines directly in the REPL, it is more convenient to use the Scala Console
instead of the Scala interpreter. The Scala Console
is also a Scala interpreter: It is the interactive shell that you can launch by executing scala
on the command line. From within the Scala IDE, select Window | Show View | Console or press Alt + Shift + Q, C. Then click on the Console selection dropdown (the last dropdown at the right side of the Console
toolbar) and select "6 Scala Console" (see Figure 2).
Figure 2: The Console
selection dropdown.
The Scala IDE will launch the Scala REPL and will display the scala>
prompt with a message that contains information about both the Scala and Java versions, similar to the following lines (see Figure 3):
Welcome to Scala version 2.10.1 (Java HotSpot(TM) 64-Bit Server VM, Java 1.7.0_21). Type in expressions to have them evaluated. Type :help for more information. scala>
Figure 3: The Scala Console
with the scala>
prompt waiting for your Scala expressions.
Now, you can enter Scala expressions with multiple lines and the Scala console will evaluate them and print the results. The following lines define a new Point3D
class. After you enter them, the Console
will display "defined class Point3D
," indicating that the class is ready to be used in new expressions.
class Point3D(val x: Double, val y: Double, val z: Double) { def move(dx: Double, dy: Double, dz: Double) : Point3D = new Point3D(x + dx, y + dy, z + dz) }
Notice that the class definition includes three parameters, which are parameters for the constructor. There is no need to define a method with the same name as the class name for the constructor. The three parameters define three fields for the Point3D
class and will be initialized when you create a new Point3D
instance. So, whenever you create a new Point3D
instance, you have to provide values for the three parameters and they will initialize the following three public immutable fields.
This way, Scala eliminates a lot of boilerplate code for the most common constructors and fields. In a single line, you defined a constructor that initializes three fields and you declared the three fields as immutable variables of type Double
. The class defines a public method named move
that generates and returns a new instance of Point3D
by using the new desired values for x
, y
, and z
as the parameters for the Point3D
constructor. This way, Point3D
is a good representation of an immutable instance that does not contain methods that change its state.