Working with Data: Literals, Values, Variables, and Types - Core Scala - Learning Scala (2015)

Learning Scala (2015)

Part I. Core Scala

Chapter 2. Working with Data: Literals, Values, Variables, and Types

In this chapter we will cover the core data and variable types in Scala. Let’s start with the definitions of the terms literal, value, variable, and type:

§ A literal (or literal data) is data that appears directly in the source code, like the number 5, the character A, and the text “Hello, World.”

§ A value is an immutable, typed storage unit. A value can be assigned data when it is defined, but can never be reassigned.

§ A variable is a mutable, typed storage unit. A variable can be assigned data when it is defined and can also be reassigned data at any time.

§ A type is the kind of data you are working with, a definition or classification of data. All data in Scala corresponds to a specific type, and all Scala types are defined as classes with methods that operate on the data.

The data stored in values and variables in Scala will get automatically deallocated by the Java Virtual Machine’s garbage collection when they are no longer used. There is no ability, or need, to deallocate them manually.

Let’s try exercising these terms by working with data in the Scala REPL. Scala values are defined with the syntax val <name>: <type> = <literal>, so we will create a value with the name x, type Int (short for “integer”), and assigned it the literal number 5:

scala> val x: Int = 5

x: Int = 5

What happened here? The REPL (again, a Read-Evaluate-Print-Loop shell) read the value definition, evaluated it, and reprinted it as a confirmation. The new value, named x, is now defined and available to use. So let’s use it:

scala> x

res0: Int = 5

scala> x * 2

res1: Int = 10

scala> x / 5

res2: Int = 1

Each of these three input lines are valid Scala syntax and return an integer value. In each case, because a value is returned, the REPL repeats the value and its type and also assigns a unique, sequentially named value starting with res0 (short for “result”). You can choose to make use of these “result” values just like any value you explicitly define:

scala> res0 * res1

res3: Int = 50

Here the values res0 and res1 are multiplied, resulting in the value 50 being returned and stored in the new value named res3.

Let’s try working with variables now. Variables, which unlike values are mutable and can be reassigned new values, are defined with the syntax var <name>: <type> = <literal>.

Here is an example of working with variables:

scala> var a: Double = 2.72

a: Double = 2.72

scala> a = 355.0 / 113.0

a: Double = 3.1415929203539825

scala> a = 5

a: Double = 5.0

In this example we defined the variable a to have the type Double, a double-precision floating-point number. And then, because it is a variable, we reassigned it to a different value.

This has been a short introduction to using values, variables, types, and literals in Scala. In the rest of this chapter we will cover each of these subject areas in depth.

Values

Values are immutable, typed storage units, and by convention are the default method for storing data. You can define a new value using the val keyword.

Syntax: Defining a Value

val <identifier>[: <type>] = <data>

Values require both a name and assigned data, but they do not require an explicit type. If the type is not specified (i.e., the “: <type>” syntax is not included), the Scala compiler will infer the type based on the assigned data.

Here are some examples of defining values with their type in the Scala REPL:

scala> val x: Int = 20

x: Int = 20

scala> val greeting: String = "Hello, World"

greeting: String = Hello, World

scala> val atSymbol: Char = '@'

atSymbol: Char = @

You may have noticed from the syntax diagram that specifying the type in value definitions is optional. In situations where it is possible to deduce the type of the value based on its assignment (for example, the literal 20 in the first example is obviously an integer), you can leave off the type from a value definition. The Scala compiler will then discern the type of the value from its assignment, a process known as type inference. Values defined without a type are not typeless; they are assigned the proper type just as if the type had been included in the definition.

Let’s try the examples again without specifying their types:

scala> val x = 20

x: Int = 20

scala> val greeting = "Hello, World"

greeting: String = Hello, World

scala> val atSymbol = '@'

atSymbol: Char = @

In this example the values end up having the same types (Int, String, and Char) as they did when the types were explicitly stated. The Scala compiler, via the REPL, was able to deduce that the literal 20 corresponds to the type Int, the literal "Hello, World" to the type String, and the literal @ to the type Char.

Using Scala’s type inference is a helpful shortcut when writing code because it removes the need to explicitly write the type of a value. As a guideline it should only be used when it does not reduce the readability of your code. In the case that someone reading your code would not be able to figure out what the type of the value is, it would be better to include the explicit type in the value definition.

Although type inference will deduce the correct type to use to store data, it will not override an explicit type that you set. If you define a value with a type that is incompatible with the initial value you will get a compilation error:

scala> val x: Int = "Hello"

<console>:7: error: type mismatch;

found : String("Hello")

required: Int

val x: Int = "Hello"

The error here affirms that an Int type cannot be used to store a String.

Variables

In computer science the term variable typically refers to a unique identifier corresponding to an allocated or reserved memory space, into which values can be stored and from which values can be retrieved. As long as the memory space is reserved, it can be assigned new values over and over again. Thus, the contents of the memory space are dynamic, or variable.

In most languages, such as C, Java, PHP, Python, and Ruby, this is the typical pattern for working with named, assignable memory storage. Variables are dynamic, mutable, and reassignable (with the exception of those defined with special restrictions such as Java’s final keyword).

In Scala, values are preferred over variables by convention, due to the stability and predictability they bring to source code. When you define a value you can be assured that it will retain the same value regardless of any other code that may access it. Reading and debugging code is easier when a value assigned at the beginning of a code segment is unchanged through the end of the code segment. Finally, when working with data that may be available for the life span of an application, or accessible from concurrent or multithreaded code, an immutable value will be more stable and less prone to errors than mutable data that may be modified at unexpected times.

The example code and exercises in this book prefer the use of values over variables. However, in those places where variables are more suitable, such as local variables that store temporary data or accumulate values in loops, variables will certainly be used.

Now that the preference for values over variables has been explained in detail, we can put that aside and cover how to use variables in Scala.

The var keyword is used to define a variable with a given name, type, and assignment.

Syntax: Defining a Variable

var <identifier>[: <type>] = <data>

Like values, variables can be defined with or without an explicit type. If no type is specified the Scala compiler will use type inference to determine the correct type to assign to your variable. Unlike values, variables can be reassigned new data at any time.

Here is an example of defining a variable and then reassigning it, in this case to the product of itself and another number:

scala> var x = 5

x: Int = 5

scala> x = x * 4

x: Int = 20

Although a variable can be reassigned, its designated type cannot, and so a variable cannot be reassigned data that has an incompatible type. For example, defining a variable of type Int and then assigning it a String value will result in a compiler error:

scala> var x = 5

x: Int = 5

scala> x = "what's up?"

<console>:8: error: type mismatch;

found : String("what\'s up?")

required: Int

x = "what's up?"

^

However, defining a variable of type Double and assigning it an Int value will work because Int numbers can be converted to Double numbers automatically:

scala> var y = 1.5

y: Double = 1.5

scala> y = 42

y: Double = 42.0

Naming

Scala names can use letters, numbers, and a range of special operator characters. This makes it possible to use standard mathematical operators (e.g., * and :+) and constants (e.g., π and φ) in place of longer names to make the code more expressive.

The Scala Language Specification defines these operator characters as “all other characters in \u0020-007F and Unicode categories Sm [Symbol/Math] … except parentheses ([]) and periods.” Square brackets (referred to in the text as parentheses) are reserved for use in type parameterization, while periods are reserved for access to the fields and methods of objects (instantiated types).

Here are the rules for combining letters, numbers, and characters into valid identifiers in Scala:

1. A letter followed by zero or more letters and digits.

2. A letter followed by zero or more letters and digits, then an underscore (_), and then one or more of either letters and digits or operator characters.

3. One or more operator characters.

4. One or more of any character except a backquote, all enclosed in a pair of back-quotes.

NOTE

Names enclosed in backquotes can, unlike the other names, be reserved keywords in Scala such as true, while, =, and var.

Let’s try out some of these naming rules in the REPL:

scala> val π = 3.14159 1

π: Double = 3.14159

scala> val $ = "USD currency symbol"

$: String = USD currency symbol

scala> val o_O = "Hmm"

o_O: String = Hmm

scala> val 50cent = "$0.50" 2

<console>:1: error: Invalid literal number

val 50cent = "$0.50"

^

scala> val a.b = 25 3

<console>:7: error: not found: value a

val a.b = 25

scala> val `a.b` = 4 4

a.b: Int = 4

1

The special character “π” is a valid Scala identifier.

2

The value name “50cent” is invalid because names cannot start with numbers. In this case the compiler started parsing the name as a literal number and ran into problems at the letter “c”.

3

The value name “a.b” is invalid because a period isn’t an operator character.

4

Rewriting this value with backquotes fixes the problem, although the aesthetics of using backquotes isn’t that great.

Value and variable names, by convention, should start with a lowercase letter and then capitalize additional words. This is popularly known as camel case, and though not required it is recommended for all Scala developers. This helps to distinguish them from types and classes which (also by convention, not by rule) follow camel case but start with an uppercase letter.

Types

Scala has both numeric (e.g., Int and Double) and nonnumeric types (e.g., String) that can be used to define values and variables. These core types are the building blocks for all other types including objects and collections, and are themselves objects that have methods and operators that act on their data.

Unlike Java and C there is no concept of a primitive type in Scala. While the Java Virtual Machine supports the primitive integer type int and the integer class Integer, Scala only supports its own integer class, Int.

Numeric Data Types

Table 2-1 displays Scala’s numeric data types.

Table 2-1. Core numeric types

Name

Description

Size

Min

Max

Byte

Signed integer

1 byte

–127

128

Short

Signed integer

2 bytes

–32768

32767

Int

Signed integer

4 bytes

–231

231–1

Long

Signed integer

8 bytes

–263

263–1

Float

Signed floating point

4 bytes

n/a

n/a

Double

Signed floating point

8 bytes

n/a

n/a

NOTE

See the API documentation for java.lang.Float and java.lang.Double for a description of the calculated maximum and minimum values for these floating-point numbers.

Scala supports the ability to automatically convert numbers from one type to another based on the rank of the type. The numeric types in Table 2-1 are sorted by their automatic conversion rank, where the Byte type is the lowest and can be converted to any other type.

Let’s try this out by creating values of different types and automatically converting them to higher-ranked types:

scala> val b: Byte = 10

b: Byte = 10

scala> val s: Short = b

s: Short = 10

scala> val d: Double = s

d: Double = 10.0

The b and s values here were assigned to new values that had a higher rank, and so were automatically converted (or “upconverted” as some say) to the higher ranks.

NOTE

Java developers will recognize the names of these types, which are wrappers around the core JVM types of the same names (except the JVM’s Integer is Scala’s Int). Wrapping JVM types ensures that Scala and Java are interopable, and that Scala can make use of every Java library.

Scala does not allow automatic conversion from higher ranked types to lower ranked types. This makes sense, because you could otherwise lose data if you convert to a type with less storage. Here is an example of trying to automatically convert a higher ranked type to a lower ranked type and the ensuing error:

scala> val l: Long = 20

l: Long = 20

scala> val i: Int = l

<console>:8: error: type mismatch;

found : Long

required: Int

val i: Int = l

You can choose to manually convert between types using the toType methods available on all numeric types. Although this makes it possible to lose data by converting to a lesser ranked type, it is useful when you know that the data is compatible with the lower ranked type.

For example, here is a Long value that can be safely converted to type Int using the toInt method, because its data is within the storage bounds of an Int:

scala> val l: Long = 20

l: Long = 20

scala> val i: Int = l.toInt

i: Int = 20

An alternative to using explicit types is to specify the type of your literal data directly, using Scala’s notation for literal types. See Table 2-2 for the full list of notations for specifying the types of literals.

Table 2-2. Numeric literals

Literal

Type

Description

5

Int

Unadorned integer literals are Int by default

0x0f

Int

The “0x” prefix denotes hexadecimal notation

5l

Long

The “l” suffix denotes a Long type

5.0

Double

Unadorned decimal literals are Double by default

5f

Float

The “f” suffix denotes a Float type

5d

Double

The “d suffix denotes a Double type

LITERAL CHARACTERS ARE CASE-INSENSITIVE

You can use either lowercase or uppercase letters in Scala’s literal types. The literal number 5L is the same as the literal number 5l.

Let’s try out these literals by assigning them to new values without stating the type. The Scala REPL will use type inference to calculate the appropriate types for each value:

scala> val anInt = 5

anInt: Int = 5

scala> val yellowRgb = 0xffff00

yellowRgb: Int = 16776960

scala> val id = 100l

id: Long = 100

scala> val pi = 3.1416

pi: Double = 3.1416

Strings

The String type represents “strings” of text, one of the most common core types in any programming language. Scala’s String is built on Java’s String and adds unique features like multiline literals and string interpolation.

Write String literals using double quotes, with special characters escaped with backslashes:

scala> val hello = "Hello There"

hello: String = Hello There

scala> val signature = "With Regards, \nYour friend"

signature: String =

With Regards,

Your friend

Like numeric types, the String type supports the use of math operators. For example, use the equals operator (==) to compare two String values. Unlike Java, the equals operator (==) checks for true equality, not object reference equality:

scala> val greeting = "Hello, " + "World"

greeting: String = Hello, World

scala> val matched = (greeting == "Hello, World")

matched: Boolean = true

scala> val theme = "Na " * 16 + "Batman!" // what do you expect this to print?

A multiline String can be created using triple-quotes. Multiline strings are literal, and so do not recognize the use of backslashes as the start of special characters:

scala> val greeting = """She suggested reformatting the file

| by replacing tabs (\t) with newlines (\n);

| "Why do that?", he asked. """

greeting: String =

She suggested reformatting the file

by replacing tabs (\t) with newlines (\n);

"Why do that?", he asked.

String interpolation

Building a String based on other values is reasonably easy to do with string addition. Here is a String built by adding text before and after the Float value:

scala> val approx = 355/113f

approx: Float = 3.141593

scala> println("Pi, using 355/113, is about " + approx + "." )

Pi, using 355/113, is about 3.141593.

A more direct way to combine your values or variables inside a String is with string interpolation, a special mode where external value and variable names are recognized and resolved. The Scala notation for string interpolation is an “s” prefix added before the first double quote of the string. Then dollar sign operators ($) (with optional braces) can be used to note references to external data.

Here is the example again using string interpolation:

scala> println(s"Pi, using 355/113, is about $approx." )

Pi, using 355/113, is about 3.141593.

You will need the optional braces if you have any nonword characters in your reference (such as a calculation), or if your reference can’t be distinguished from the surrounding text:

scala> val item = "apple"

item: String = apple

scala> s"How do you like them ${item}s?"

res0: String = How do you like them apples?

scala> s"Fish n chips n vinegar, ${"pepper "*3}salt"

res1: String = Fish n chips n vinegar, pepper pepper pepper salt

An alternate format for string interpolation uses printf notation, very useful when you want to control the data formatting such as the character count or display of decimal values. To use printf notation change the prefix to an “f” and follow the end of the reference immediately with theprintf notation:

NOTE

If you are unfamiliar with printf there are numerous online references for the format, including the official Javadoc for java.util.Formatter, the underlying engine used by Scala to format these strings.

scala> val item = "apple"

item: String = apple

scala> f"I wrote a new $item%.3s today"

res2: String = I wrote a new app today

scala> f"Enjoying this $item ${355/113.0}%.5f times today"

res3: String = Enjoying this apple 3.14159 times today

These printf notations make the references a little harder to read than in the previous examples, but do provide essential control over the output.

Now that we have learned how to control data output with strings, let’s find out how to do the opposite with regular expressions.

Regular expressions

A regular expression is a string of characters and punctuation that represents a search pattern. Popularized by Perl and command-line utilities like Grep, regular expressions are a standard feature in the libraries of most programming languages including Scala.

The format for Scala’s regular expressions is based on the Java class java.util.regex.Pattern. I recommend reading the Javadoc (the Java API documentation) for java.util.regex.Pattern if you are unfamiliar with this type, because Java’s (and thus Scala’s) regular expressions may be different from the format you have used with other languages and tools.

The String type provides a number of built-in operations that support regular expressions. Table 2-3 displays a selection of these operations.

Table 2-3. Regular expression operations

Name

Example

Description

matches

"Froggy went a' courting" matches ".* courting"

Returns true if the regular expression matches the entire string.

replaceAll

"milk, tea, muck" replaceAll ("m[^ ]+k", "coffee")

Replaces all matches with replacement text.

replaceFirst

"milk, tea, muck" replaceFirst ("m[^ ]+k", "coffee")

Replaces the first match with replacement text.

For more advanced handling of regular expressions, convert a string to a regular expression type by invoking its r operator. This will return a Regex instance that can handle additional search and replace operations as well as capture group support. A capture group makes it possible to select items in a given string and convert them to local values based on the regular expression pattern. The pattern must include at least one capture group defined by parentheses, and the input must include at least one of the captured patterns to return the value.

Syntax: Capturing Values with Regular Expressions

val <Regex value>(<identifier>) = <input string>

Let’s try this out by capturing the numeric value from the output of the previous example (see String interpolation). We’ll use multiline strings to store our regular expression pattern, because they are literal and allow us to write a backslash without a second, escaping backslash:

scala> val input = "Enjoying this apple 3.14159 times today"

input: String = Enjoying this apple 3.14159 times today

scala> val pattern = """.* apple ([\d.]+) times .*""".r 1

pattern: scala.util.matching.Regex = .* apple ([\d.]+) times .* 2

scala> val pattern(amountText) = input 3

amountText: String = 3.14159

scala> val amount = amountText.toDouble 4

amount: Double = 3.14159

1

The capture group is the series of digits and a period between the words apple and times.

2

The full regular expression type is scala.util.matching.Regex, or just util.matching.Regex.

3

The format is admittedly a bit odd. The name of the new value containing the capture group match, amountText, does not directly follow the val identifier.

4

After converting the amount in text form to a Double we have our numeric value.

Regular expressions serve as a compact and efficient means to process text, with operations such as matching, replacing, and capturing. If you are still new to regular expressions, it is worth investing time to study them because they are widely applicable in modern software development.

An Overview of Scala Types

In this section we will move on from numbers and strings to a broader look at the range of core types. All of Scala’s types, from numbers to strings to collections, exist as part of a type hierarchy. Every class that you define in Scala will also belong to this hierarchy automatically.

Figure 2-1 shows the hierarchy of Scala’s core (numeric and nonnumeric) types.

The Scala Type Hierarchy

Figure 2-1. The Scala type hierarchy

The open-headed arrows in the diagram indicate supertypes, a common notation in object-oriented diagrams. The multiple-arrow types at the bottom indicate that they are subtypes of every type in the system, including classes you define on your own.

In Table 2-4 you can see a full listing of the specific types mentioned in this diagram, followed by more complete descriptions.

Table 2-4. Core nonnumeric types

Name

Description

Instantiable

Any

The root of all types in Scala

No

AnyVal

The root of all value types

No

AnyRef

The root of all reference (nonvalue) types

No

Nothing

The subclass of all types

No

Null

The subclass of all AnyRef types signifying a null value

No

Char

Unicode character

Yes

Boolean

true or false

Yes

String

A string of characters (i.e., text)

Yes

Unit

Denotes the lack of a value

No

The Any, AnyVal, and AnyRef types are the root of Scala’s type hierarchy. Any is the absolute root, and all other types descend from its two children, AnyVal and AnyRef. The types that extend AnyVal are known as value types because they are the core values used to represent data. They include all of the numeric types we have covered plus Char, Boolean, and Unit. AnyVal types are accessed just like other types but may be allocated at runtime either on the heap as objects or locally on the stack as a JVM primitive value. All other types have AnyRef as their root and are only ever allocated on the heap as objects. The term “Ref” in “AnyRef” indicates they they are reference types that are accessed via a memory reference.

At the bottom of the Scala type hierarchy are the Nothing and Null types. Nothing is a subtype of every other type and exists to provide a compatible return type for operations that significantly affect a program’s flow. For example, the return keyword, which exits a function early with a return value, has a return type of Nothing so it can be used in the middle of initializing a value and not affect the type of that value. Nothing is only used as a type, because it cannot be instantiated.

The other bottom type is Null, a subtype of all AnyRef types that exists to provide a type for the keyword null. A String variable, for example, can be assigned null at any time, such that the variable does not point to any string instance in memory. This assignment of null to a variable declared as type String is acceptable because null is a compatible type for String. Defining a type for null is an example of how Scala’s syntax prefers the use of real types and instances to reserved keywords.

Char is the only type that could also appear in Numeric Data Types. As the basis of the String type it contains a single character and so is sometimes considered to be a unit of text. Essentially it is a scalar type that can be converted to and from other numbers.

Char literals are written with single quotes, distinguishing them from String literals, which are written with double quotes. If you’re familiar with the ASCII character numbering system, this example should be obvious:

scala> val c = 'A'

c: Char = A

scala> val i: Int = c

i: Int = 65

scala> val t: Char = 116

t: Char = t

The Boolean type is limited to the values true and false. In addition to using true and false, you can also obtain Boolean values from comparison and Boolean logic operators:

scala> val isTrue = !true

isTrue: Boolean = false

scala> val isFalse = !true

isFalse: Boolean = false

scala> val unequal = (5 != 6)

unequal: Boolean = true

scala> val isLess = (5 < 6)

isLess: Boolean = true

scala> val unequalAndLess = unequal & isLess

unequalAndLess: Boolean = true

scala> val definitelyFalse = false && unequal

definitelyFalse: Boolean = false

WHAT IS THE DIFFERENCE BETWEEN & AND && ?

The Boolean comparison operators && and || are lazy in that they will not bother evaluating the second argument if the first argument is sufficient. The operators & and | will always check both arguments before returning a result.

Unlike many dynamic languages, Scala does not support automatic conversion of other types to Booleans. A nonnull string cannot be evaluated as true, and the number zero does not equal false. If you need to evaluate a value’s state to a Boolean, use an explicit comparison:

scala> val zero = 0

zero: Int = 0

scala> val isValid = zero > 0

isValid: Boolean = false

The Unit type is unlike the other core types here (numeric and nonnumeric) in that instead of denoting a type of data it denotes the lack of data. In a way it is similar to the void keyword used in Java and C, which is used to define a function that doesn’t return data. The Unit type is similarly used in Scala as the return type for functions or expressions that don’t return anything. For example, the common println function could be said to return a Unit type because it returns nothing.

The Unit literal is an empty pair of parentheses, (), which if you consider it is a fine representation of not having a value. If you want you can define a value or variable with the Unit type, but again its common usage is for defining functions and expressions:

scala> val nada = ()

nada: Unit = ()

Now that we have covered the core types, let’s have a look at the operations they all have in common.

Type operations

Table 2-5 displays the operations available on all types in Scala. The toString and hashCode methods are required on all JVM instances.

Table 2-5. Common type operations

Name

Example

Description

asInstanceOf[<type>]

5.asInstanceOf[Long]

Converts the value to a value of the desired type. Causes an error if the value is not compatible with the new type.

getClass

(7.0 / 5).getClass

Returns the type (i.e., the class) of a value.

isInstanceOf

(5.0).isInstanceOf[Float]

Returns true if the value has the given type.

hashCode

"A".hashCode

Returns the hash code of the value, useful for hash-based collections.

to<type>

20.toByte; 47.toFloat

Conversion functions to convert a value to a compatible value.

toString

(3.0 / 4.0).toString

Renders the value to a String.

AVOID ASINSTANCEOF

The asInstanceOf operation will cause an error if the value cannot be converted to the requested type. To avoid runtime errors with this operation, prefer the to<type> typed conversion operations when possible.

The types we have covered so far in this chapter are all (with the possible exception of String) scalar values, which represent a single element (or, of course with Unit, the lack of any element). As a complement to these scalar values, we will finish the chapter with the Tuple type, which can collect two or more of these values into a new, ordered element.

Tuples

A tuple is an ordered container of two or more values, all of which may have different types. You may be familiar with this term from working with relational databases, where a single row of a table is considered its own tuple. Tuples can be useful when you need to logically group values, representing them as a coherent unit. Unlike lists and arrays, however, there is no way to iterate through elements in a tuple. Its purpose is only as a container for more than one value.

You can create a tuple by writing your values separated by a comma and surrounded by a pair of parentheses.

Syntax: Create a Tuple

( <value 1>, <value 2>[, <value 3>...] )

For example, here is a tuple containing Int, String, and Boolean values:

scala> val info = (5, "Korben", true)

info: (Int, String, Boolean) = (5,Korben,true)

You can access an individual element from a tuple by its 1-based index (e.g., where the first element is 1, second is 2, etc.):

scala> val name = info._2

name: String = Korben

An alternate form of creating a 2-sized tuple is with the relation operator (->). This is a popular shortcut for representing key-value pairs in tuples:

scala> val red = "red" -> "0xff0000"

red: (String, String) = (red,0xff0000)

scala> val reversed = red._2 -> red._1

reversed: (String, String) = (0xff0000,red)

Tuples provide a generic means to structure data, and are useful when you need to group discrete elements for handling.

Summary

This may be a challenging chapter to see through to the end, because you had to read all about types and data without learning how to do real programming in Scala yet. I’m glad you did.

What was the oddest or most-unexpected part of this chapter? The use of keywords to announce value and variable definition? The reversed manner (if you’re coming from Java) of defining a variable’s name before its type? The idea that much of your code can use fixed, nonreassignable values instead of (variable) variables?

If these ideas were hard to take, the good news is that, as you gain experience in Scala developemnt, they will become quite normal. Eventually they may even seem to be obvious choices for a well-designed functional programming language.

At this point you should know how to define your own values and variables, although we haven’t yet learned where to come up with useful data to store in them. In the next chapter you will study ways to derive and calculate this data using logical structures known as expressions.

Exercises

1. Write a new Centigrade-to-Fahrenheit conversion (using the formula (x * 9/5) + 32), saving each step of the conversion into separate values. What do you expect the type of each value will be?

2. Modify the Centigrade-to-Fahrenheit formula to return an integer instead of a floating-point number.

3. Using the input value 2.7255, generate the string “You owe $2.73.” Is this doable with string interpolation?

4. Is there a simpler way to write the following?

5. val flag: Boolean = false

val result: Boolean = (flag == false)

6. Convert the number 128 to a Char, a String, a Double, and then back to an Int. Do you expect the original amount to be retained? Do you need any special conversion functions for this?

7. Using the input string “Frank,123 Main,925-555-1943,95122” and regular expression matching, retrieve the telephone number. Can you convert each part of the telephone number to its own integer value? How would you store this in a tuple?