4 Values and variables

After reading this lesson, you’ll be able to

  • Write code that uses values to save and reuse computational results
  • Implement programs that carefully take advantage of variables

Scala has a clear separation between mutable and immutable assignments. In Scala, values are immutable: you cannot modify them after creating them. Variables are mutable: they can refer to different instances over time. Deciding when to declare a value rather than a variable is essential for your code to be fast and bug-free. Variables are more straightforward to use in your code because you can modify them. However, they can make your program extremely difficult to maintain and lead to errors when different processes try to do so simultaneously. Values can be challenging to use because you cannot modify them once created. But they can make your program easier to understand: their assignments never change, so you can easily predict and test their evaluation. They also guarantee that your code will not be affected by concurrency issues, such as data inconsistencies, resources starvation, and deadlocks, when accessed by several threads. A fragment of code that multiple processes can access without causing concurrency issues is thread-safe. In the capstone for this unit, you’ll define both values and variables to name fragments of code and make your program more readable.

Consider this

Consider the other languages you have encountered so far in your career. Do they make a clear distinction between mutable and immutable assignments? If so, how? If not, can you think of the advantages and disadvantages of not having a clear separation between the two?

4.1 Values

Assignments improve your code’s readability by breaking down the computation and assigning meaningful names to it. They can also increase your program’s performance by saving intermediate results that you can reuse and share between processes. Values are the most commonly used type of assignments in Scala.

In the next example, you’ll see how values can improve your code’s readability, particularly when dealing with complex scenarios. Suppose you are performing some calculation to mark exams with the following requirements: each exam has three questions, each with a possible score of 3 points, and its final mark must be between 1 and 10. Several solutions are possible; let’s pick one of them. You could calculate the average score for all the questions and then convert it to a number from 1 to 10. For example, you should follow these steps for three questions scoring 1.5, 2, and 2.5:

  1. You compute the average score for all the questions: (1.5 + 2 + 2.5) / 3 is 2.

  2. You scale the average score from 1 to 10. Considering that the maximum score possible for each question is 3, then you can rescale it as follows: 2 * 10 / 3 is about 6.6.

  3. You round 6.6 up to the closest integer and obtain 7.

Let’s translate this procedure into code.

Listing 4.1 Marking an exam

def markExam(q1: Double, q2: Double, q3: Double) = 
  Math.round(((q1 + q2 + q3) / 3) * 10 / 3)

The function markExam replicates the calculations described, but it is difficult to identify its objective and computation steps. You can improve the readability of the markExam function by using values with descriptive names.

Listing 4.2 Marking an exam—a more readable version

def markExam(q1: Double, q2: Double, q3: Double) = {
  val avgScore = (q1 + q2 + q3) / 3
  val scaledScore = avgScore * 10 / 3
  Math.round(scaledScore)               
}

No need for the return keyword; the last expression is the one returned.

Values are a fundamental component of your code. In Scala, you declare them by using the keyword val. You can only initialize them once because they are immutable; if you try to reassign them, the code will not compile. Consider the following example:

scala> val a = 1
val a: Int = 1
 
scala> a
val res0: Int = 1
 
scala> a = 2
<console>:11: error: reassignment to val     

You cannot reassign a value, which is an immutable assignment.

  

Quick Check 4.1 Can you use the += operator with a value of type Int? What about the operator -=?

When declaring a value, its type is optional; the compiler tries to infer it for you. If the compiler infers an unexpected type, you can provide it explicitly; the compiler will check if the type you specified is compatible with the assignment of your value and eventually return a compilation error:

scala> val b: Int = 1
val b: Int = 1
 
scala> val c: Double = 1               
val c: Double = 1.0
 
scala> val d: Int = "not-an-int"       
       error: type mismatch;
        found   : String("not-an-int")
        required: Int
 
scala> val d: Any = "not-an-int"     
val d: Any = not-an-int

The compiler knows how to fit an Int into a Double.

You cannot assign a String to an Int.

Any is the root of the class hierarchy: you can assign any value to it.

Look at figure 4.1 for a syntax diagram on values in Scala.

Figure 4.1 Syntax diagram of how to define a value in Scala. A value is immutable, so you can assign it only once.

Quick Check 4.2 Which of these expressions are valid? Use the REPL to validate your hypotheses.

  1. val test: Double = 2.0

  2. val test: Int = 2.0

  3. val test: Any = 2.0

4.2 Variables

In the previous section, you wrote a function to mark an exam. Let’s expand it so that you can compute the average score of all exams you marked so far. Values are immutable, so you cannot use them to keep track of how your stats evolve, which is the mutable state of your program. In these cases, you can use variables.

Listing 4.3 Keeping track of the mark statistics

var marksSum = 0                             
var marksCount = 0                           
 
def averageMark: Double = 
  marksSum.toDouble / marksCount             
 
def markExam(q1: Double, q2: Double, q3: Double): Int = {
  val avgScore = (q1 + q2 + q3) / 3
  val scaledScore = avgScore * 10 / 3
  val mark = Math.round(scaledScore).toInt   
 
  marksSum += mark
  marksCount += 1
  mark                                       
}

Example of a variable

When converting marksSum to Double, the result of your division must be a Double. An integer divided by another integer produces an Int while diving a double by an integer returns a double.

Math.round results in a constant of type Long, so you need to convert it to Int.

There’s no need for the return keyword; the last expression is the one returned.

In Scala, variables are very similar to values: while you can assign values only once, you can reassign variables multiple times. Consider the following example:

scala> var a = 1
var a: Int = 1
 
scala> a
val res1: Int = 1
 
scala> a = 2
// mutated a

You do not need to provide a type for your variable; the compiler will infer it for you. When doing so, the compiler makes sure that the assignment is compatible with it. Look at figure 4.2 for a summary of how to define variables in Scala.

Figure 4.2 Syntax diagram of how to define a variable in Scala. A variable is a mutable assignment: you can reassign it more than once.

Quick Check 4.3 Can you use the += operator with a var of type String? What about the operator -=?

  

Think in Scala: val or var?

When in doubt, you should always try to use val instead of var.

Having immutable values makes your programs safe from concurrency inconsistency in exchange for some additional memory allocation; most of the time, you will not notice this extra memory usage thanks to the JVM’s garbage collection.

Using mutable structures makes your programs challenging to test and reason about; you need to pay attention to how processes share data and how they evolve. Mutable structures may be more performant, but they also expose your code to complex concurrency issues such as data inconsistency, deadlocks, and resource starvation.

Before choosing to stick with a var, you should make sure you have tried everything else in your book of tricks; variables should be your last and least desirable option.

At this point in your Scala journey, you may think that using mutability is often the only option. But this is not the case: in future units and lessons, you’ll discover many tools and techniques, such as case classes, that will allow you to get rid of var from your code.

Summary

In this lesson, my objective was to teach about the different types of assignments in Scala.

  • You have learned how to declare values and that you can only assign them once.

  • You have seen how to define variables and how you can reassign them multiple times.

Let’s see if you got this!

Try This Modify your markExam function to keep track of both the lowest and the highest mark computed so far.

Answers to quick checks

  

Quick Check 4.1 The operators += and -= cannot be used with val: a value is immutable and once assigned you cannot reassign it.

  

Quick Check 4.2 The first and third expressions are valid, while the second one is invalid:

  1. Valid: an expression of type Double is compatible with Double.

  2. Invalid: the compiler doesn’t know how to fit an expression of type Double into an Int. Even if the quantity 2.0 is equal to 2 from a mathematical perspective, the compiler doesn’t automatically convert a Double to Int to avoid precision loss; 2.5 is not equivalent to neither 2 or 3. Note that the opposite (i.e., assigning an Int expression to a Double) is valid.

  3. Valid: Any is compatible with any type because it is at the root of Scala’s class hierarchy.

  

Quick Check 4.3 You can use the operator += together with a var of type String; you can reassign a variable, and the class String has an implementation for the function +. You cannot use the operator -= with a var of type String because the class String doesn’t have a method called “-”.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.134.118.95