The ancient Chinese philosopher, Lao Tzu, once said, “The journey of a thousand miles begins with a single step.” Recognizing that there is a long way to go in your journey toward learning software engineering, this chapter may certainly be considered your first step as you begin to venture into the Scala language. And while taking that first step is indeed a momentous and commendable occasion, ensuring that you are headed in the appropriate direction is also a critical consideration.
There is an archetypal and yet naive notion within the industry that the best way to learn how to program is to pick a problem that you are trying to solve and write an application to solve it. By relating what you are learning to something with practical application, this method keeps fledgling developers engaged and consciously reinforces the concepts learned. However, what if the problem that you really want to solve relates to streaming a virtual reality environment across the globe in real time? Learning the vast quantities of methods and paradigms necessary to solve that problem would be like drinking from a fire hose. The counterargument might be to simply pick a reasonable sized problem to solve as an alternative. But what does that mean to a beginner? How would a beginner know what a reasonable sized problem is without already having the programming knowledge necessary to accomplish the task?
Given this paradoxical conundrum, it would stand to reason that the primary focus for your departure on this metaphorical journey should be ensuring that the concepts you learn first are easily digestible. For that reason, the programming constructs that we will start with in this book will be related to arithmetic, since that will provide a common basis of knowledge from which to draw upon. So, does that mean you need to be really good at math? By no means. The arithmetic that will be used in this book will be very basic, so do not be intimidated. Also, the remainder of the book will not have nearly as much math as this chapter as we start to move on to more concrete concepts. So, if you are not a fan of math, just stick with it as these foundational concepts are crucial. That being said, in this chapter we will first cover basic mathematical expressions followed by variable assignments and substitution, similar to concepts you might have learned in a pre-algebra class.
Basic Expressions
The most basic building block of software engineering is the expression. An expression, in both computer science and mathematics, is the combination of symbols that when evaluated will produce a fixed result. In Scala, everything can be distilled down to a series of expressions. Understanding this will become incredibly valuable in the future as you learn to test and debug your code because you will be able to isolate individual expressions from the overall program for separate evaluation and assessment. Let’s program a few basic expressions in Scala to help further demonstrate what they are and why they are useful.
In order to do that, it’s imperative to understand your Scala environment and its behavior. To help you understand, we will first walk through a couple of examples that produce expected results and how best for you to interpret those results. After that, we will walk through a few unexpected results so that you can learn how to get around road blocks with basic expressions before moving on to more complex expressions.
The Scala REPL and a basic mathematical expression
After typing in 2 + 2 and hitting enter, the interpreter will evaluate the expression and return to you the answer 4, much like a calculator. Any syntactically valid expression will follow this same pattern within the Scala REPL. Unlike a calculator, you’ll notice a couple of extra things in the “Print” step of the interpreter.
First, there is the symbol res0. The res symbol stands for “result” and the 0 is an ordered unique identifier (also known as an index) that denotes that this is the first result in the REPL session. A session is an individual Scala REPL that is open for a unit of time. Subsequent results in the session will increment that number by one for each interpreted expression. When you close your terminal or quit the Scala REPL (by typing in :quit), that will end your session and the number will start back over at 0 for future sessions. You might wonder, if it’s the first result, then why is the unique identifier a zero and not a one? Influenced by binary among other things, most incremental counting indexes in computer science start at zero.
Second, there is the symbol Int which stands for Integer. If you remember from your mathematics education, an integer is any whole number. The symbol is just the interpreter telling you that it has inferred the type of the evaluated expression result and that type is an integer. We will cover more on the different types in later chapters, but for now you can be content to understand that Scala is inferring types for you.
Example of a multiplication expression with invalid syntax
You’ll notice that the interpreter has printed out three lines of response as a result of the input expression. The first line provides an error message in an attempt to help us understand why we did not get the result we expected and how we might go about debugging it. Because we haven’t covered members or types, you should not be expected to understand the meaning of the error message at this point. The second line reprints your original expression so that the third line can point out where the interpreter encountered the error (as denoted by the caret ^ symbol). The caret and the error message seem to be pointing us to the x in the expression. Intuition would suggest that it seems to be implying that we have a syntax error. As we covered in the previous chapter, if a sentence or code block is combined in the wrong order or with the wrong punctuation, the underlying meaning might change or have no meaning at all. Computers cannot interpret code unless it is written exactly as specified by the syntax rules of the language.
Example of valid multiplication expression
Note
You might notice that in the listings in this chapter there are spaces separating the operators and the operands for each expression. These spaces are known as “whitespace” in programming. In some languages, most notably Python, whitespace is an important part of the syntax and meaning is derived from their use. In Scala, however, the whitespace has no meaning to the interpreter or the compiler and is therefore ignored in simple expressions like these. Deciding whether or not to have extra whitespace in your code is a personal preference, but most organizations will prefer you to be consistent with their coding style.
Examples of valid division expressions
You might look at the second example and question whether or not it is indeed proper syntax given the answer. After all, 4 / 5 on a standard calculator evaluates to 0.8. This is the quintessential example of the difference between a syntax error and a semantic error in computer science. The syntax is absolutely correct – it has two operands separated by a symbol that represents an appropriate operator. Yet, we did not get the result we wanted. When the meaning of your expression and therefor the result is not what you intended, there is sure to be a semantic error somewhere. It just so happens that with semantic errors, Scala does not return any error results back to you in the terminal window because it assumes that, since what you typed in as input was a syntactically valid expression, it is exactly what you meant to type.
Examples of division expressions with non-integers
You’ll notice that although 4.0 and 5.0 are, in fact, whole number since they do not have any remainders in the decimal position, by adding the decimal and the zero, it helps the Scala interpreter know that you intended for the type of these operands to be decimals that can accept remainders. The type of the result of these expressions is a Double, which we will cover in more detail in later chapters. For now, all you need to know is that a Double type can accept partial decimals where integers cannot.
Now that you’ve seen a few examples of basic expressions with different outcomes, we can move on to more advanced expressions to demonstrate the broader set of syntax rules surrounding Scala expressions.
Advanced Expressions
In order to best represent advanced expressions in Scala, I have found it useful to use popular mathematical formulas that most people are familiar with. The formulas that will be used for the remainder of this chapter in the examples and exercises will be the basic linear equation, Einstein’s theory of relativity, and the Pythagorean Theorem. If you are not familiar with these equations, it might be useful for you to go look them up, but we will cover their definitions briefly as well.
A representation of the linear equation expression
So, what we’ve discovered as a result of this expression is that on this particular line, when the X coordinate is 8, the Y coordinate is 7. In this example, you can see that the result type is a Double since we used a fractional operand in the expression. You might also notice that for this particular expression there are multiple operators and they are evaluated from left to right. You can imagine an even more advanced scenario where you might need to chain several more operations together in this manner. We’ll start to see such examples later on in the chapter, but for now just ensure that you understand how to use the information gained to solve a simple linear equation.
Exercise 4-1
- 1.
All other factors being the same, if the X coordinate is 3.2, what is the Y coordinate?
- 2.
If the slope changed to 2 and the X coordinate is 4, what is the Y coordinate? What is the type of the result?
- 3.
Keeping the slope at 2, if the Y coordinate is 5, what is the X coordinate (this will take a bit of algebra to re-arrange the formula)?
A representation of Einstein’s mass-energy equivalence formula
In this expression, the answer is shown in scientific notation. You might very well be used to seeing scientific notation expressed in terms of 8.9875517873681766 x 1017 joules. All you need to know in Scala is that the “x 10” part is replaced with an “E” and even though the 17 does not appear to look like it is an exponent, you can safely assume that it is. Knowing how scientific notation is expressed in the Scala REPL is important, but more important than that is the demonstration of exponent syntax in this example. In this exponent expression, the speed of light constant is the operand and the operators are Math.pow (which tells Scala you want to raise your operand to a certain power), the parentheses, the comma, and the 2, which is the power you want to raise your operand to. If you wanted to raise an operand to the power of 3, you would simply use something like Math.pow(2,3) which is the equivalent of 23 and would yield an answer of 8.0.
- 1.
Evaluate expressions within parentheses first and simplify the terms inside them.
- 2.
Evaluate all exponents.
- 3.
Evaluate multiplication or division. If an expression contains both, evaluate left to right.
- 4.
Evaluate addition or subtraction. If an expression contains both, evaluate left to right.
Exercise 4-2
Take a moment to see if you can extrapolate the information you’ve gained so far to represent the following expression in Scala:
(42 + 3)(22 – 4)
Note that the two sets of parentheses could represent factors in a polynomial equation. As you code your answer, consider the order of operations in which Scala will evaluate your expression.
Literal expression of the Pythagorean Theorem
Notice the new syntax to denote the square root in a similar fashion to the exponent syntax, Math.sqrt. It will simply take the square root of anything inside its parentheses. An imperative observation to note here is that expressions can contain other expressions just as in mathematics. This notion allows for ever-increasing complexity of expressions. It’s also a useful concept to grasp when attempting to debug an expression. If the answer you received seemed less than intuitive, you could simple pull the individual expressions out of the larger expression and evaluate them independently to ensure everything is operating as you would have expected. In evaluating this expression, given the order of operations rules, the exponents of 42 and 62 are evaluated first, then the terms inside the square root parentheses are simplified by adding them together, and finally the square root is taken from the result giving us the answer of approximately 7.211.
Exercise 4-3
- 1.
What is the hypotenuse of a right triangle if a = 7 and b = 9?
- 2.
Given a right triangle with a base side equal to 3 and its hypotenuse equal to 30, what is the area of the triangle?
Hint: To solve this, you will need to understand what the length of the third side is as the formula to solve for the area of the triangle is 1/2*base*height.
Now that you have seen a few examples of complex expressions, the next step is to gain a better understanding of the rest of the Scala operators that are possible. These can be grouped into three categories including arithmetic operators, comparison operators, and logical operators. After you have absorbed the universe of operators, you will be introduced to variables which will further expound upon the vast capabilities of expressiveness that is available in the Scala language.
Arithmetic Operators
An example of the subtraction and modulo operators
Notice that the subtraction occurs first as the expression inside the parentheses obtains the evaluation priority. Parentheses are considered grouping operators in this scenario. Next, the modulo evaluates that 11 divided by 3 would yield the integer 3 with a remainder of 2, and thus it returns the integer 2 as the final result. The modulo operator is extremely useful when trying to evaluate whether a certain number is considered even or odd because any number that is divided by 2 with no remainder is even and any number that has a remainder is odd. It would be prudent to make a mental note of this fact as it will likely come in handy in your career as a software engineer.
A list of arithmetic operators
Operator | Name | Description |
---|---|---|
+ | Addition | Returns the sum of the operands on either side. |
- | Subtraction | Returns the difference between the operands on either side. |
* | Multiplication | Returns the product of the operands on either side. |
/ | Division | Returns the quotient of the operands on either side. |
( ) | Parentheses | Groups expressions for prioritized evaluation. |
Math.pow(n,m) | Exponent | Returns the evaluation of n raised to the m power. |
Math.sqrt(n) | Square root | Returns the square root of n. |
Math.abs(n) | Absolute value | Returns the absolute value of expression n. |
Math.round(n) | Rounding | Returns the closest integer of the result of expression n. |
Math.log(n) | Logarithm | Returns the natural logarithm of the result of expression n. |
Comparison Operators
A list of comparison operators
Operator | Name | Description |
---|---|---|
> | Greater than | Expression on the left is greater than the expression on the right. |
< | Less than | Expression on the left is less than the expression on the right. |
>= | Greater than or equal to | Expression on the left is greater than or equal to the expression on the right. |
<= | Less than or equal to | Expression on the left is less than or equal to the expression on the right. |
== | Equal | Both expressions are equal. |
!= | Not equal | The two expressions are not equal. |
Examples of comparison operators
Look through each of these expressions carefully. As you can see, some of the arithmetic operations from the previous section have been added in to help express these inequality equations. You might also notice the type of the response is Boolean. We will cover types in later chapters, but for now all you need to know is that Scala has inferred the type of the response as a true/false value called Boolean. Some of these might not be terribly intuitive at first. Ensure that you comprehend why each of these returned the evaluated response before moving forward. If you don’t understand, go back and review the chapter again before moving on to logical operators.
Logical Operators
A list of logical operators
Operator | Name | Description |
---|---|---|
&& | And | Evaluates whether the operands on either side are both true and returns a true value if so; otherwise, it returns false. |
|| | Or | Evaluates whether either operand that surrounds it is true and returns a true value if so. If both operands are false, it returns false. |
! | Not | Evaluates the operand that it immediately precedes to the opposite of its true/false value. |
Examples of logical operators
The first expression in this example evaluates to false because in order for the expression as a whole to evaluate to true, both sides of the operator must also be true. Because 4 is not greater than 5, the left side of the operator evaluates to false and the expression as a whole is therefore false. In the second expression, you’ll notice that 4 > 5 is put inside parentheses so that it will first be evaluated before the Not operator is applied to it. By applying the Not operator, the false expression on the left of the And operator flips from false to true, therefore making the expression as a whole true. In the last two expressions, only one side of the Or operator needs to be true for the expression as a whole to evaluate to true. The first Or expression has one true value and is therefore true, and the second Or expression has false expressions on both sides of the operator so the overall expression value is false.
The combination of these logical operators along with comparison operators and arithmetic operators can yield some extraordinarily complex expressions, as you might have guessed. In such complex expressions, reading and understanding the equation often becomes unwieldy. For this reason, among a plethora of others, the next section will introduce you to variables which allow for abstracting out pieces of the expression and storing their evaluated result for later use. This provides the benefit of being able to easily break up an expression into smaller, more digestible parts and also allows for the reuse of an expression evaluation.
Variables
Just like in your early math classes, programming has a concept of a variable that allows for storing a dynamic result or value in a symbol. Many math students tend to be intimidated by the variety of symbols that get introduced to their arithmetic tool set and mentally check out as soon as they see a letter in an equation. However, variables actually make programming much easier as their existence prevents expressions from infinitely chaining together.
Scala has two variable types that you will need to be familiar with. The first is denoted by the keyword val and the second is denoted by the keyword var. The difference between the two has to do with a term known as mutability. Mutability is the concept that determines whether a variable can be changed once it has been assigned. The val variable cannot be changed once it has been assigned and is therefore said to be immutable. Conversely, the var variable can be reassigned and is therefore considered mutable. Why there are two variable types and which one you should use is a topic that will be discussed later on in the book. For now, I want you to keep your focus on the usefulness that variables bring to expressions.
The Pythagorean Theorem with variables introduced as substitutes
Note
Something in Listing 4-12 may seem different to you. Notice that the responses for variable assignment do not include the res symbol anymore but rather the symbol in which you assigned them. You might be enticed to deduce, then, that the results of the expressions you have been evaluating up to this point have been stored in the computer’s memory as variables with the res keyword. If that is something that crossed your mind, you’ll be delighted to find out that it is in fact true. You can reuse any of your previous expression results using the keyword that it returns. Take a moment to give this a try in your REPL by combining previous expression results into new expressions.
Sub-expressions stored as variables
As you can see, not only is there the added benefit of extracting expressions for simplification and readability, but you can also see the results of the sub-expression as they are assigned. As stated before, this will be incredibly useful for you in the future as you begin to break up complex problems into smaller pieces for debugging purposes.
Demonstration of various assignment operators
A list of assignment operators
Operator | Name | Description |
---|---|---|
= | Variable assignment | Sets an expression result equal to a variable. Not to be confused with the == comparison operator. |
+= | Increment | Increments the value of a variable and reassigns the variable to the incremented result. |
-= | Decrement | Decrements the value of a variable and reassigns the variable to the decremented result. |
/= | Divide and reassign | Divides the value of a variable and reassigns the variable to the result of the division. |
*= | Multiply and reassign | Multiplies the value of a variable and reassigns the variable to the result of the multiplication. |
Summary
In this chapter, you were introduced to the Scala REPL and you were able to do some actual coding, albeit mostly mathematical. You were introduced to the concept of expressions, both mathematical and their Scala equivalent, and you got to know some of the basic Scala syntax surrounding operators and operands. These concepts were reinforced by applying them to a basic linear equation, Einstein’s mass-energy equivalence formula, and the Pythagorean Theorem. Finally, you were shown how these expressions can be stored in a computer’s short-term memory for later reuse using variable assignment.
In the next chapter, we’ll move away from all the numbers and do some interesting things with text and groups of data. You’ll also be introduced to different basic data types and learn a few reasons why they are important. As usual, make sure that you can do all of the basic concepts in this chapter in your sleep before moving on.