Mike Slinn

Learning Scala Using The REPL 2/3

— Draft —

Published 2013-12-21. Last modified 2024-07-18.
Time to read: 6 minutes.

This lecture continues our exploration of simple Scala programs. Looping constructs and other control statements are also discussed.

Exploring Scala with the REPL

The REPL is useful for interactively experimenting with an incantation before you write some code. This is a much more productive way of working than the edit / compile / debug loop that you would otherwise have to perform.

Literals

Scala supports literals for integer numbers, floating point numbers, characters, booleans, symbols, and strings. The syntax for Scala literals is similar to Java literals.

Integers

Values of type Int are all integer numbers between −231−231 and 231−1231−1, inclusive. Values of type Long are all integer numbers between −263−263 and 263−1263−1, inclusive. A compile-time error occurs if an integer literal denotes a number outside these ranges.

Integer literals are usually of type Int, or of type Long when followed by a L or l suffix. (Lowercase l is deprecated for reasons of legibility.)

The numeric ranges given by these types are:

Byte −27 to 27−1
Short −215 to 215−1
Char 0 to 216−1

The digits of a numeric literal may be separated by arbitrarily many underscores for purposes of legibility.

0 21_000 0x7F -42L 0xFFFF_FFFF

 – Scala Specification

Let’s create some integer variables using the Scala REPL:

Scala REPL
scala> 5+2
res0: Int = 7 
scala> val zero = 0 zero: Int = 0
scala> 5 * zero res1: Int = 0
scala> val zero: Int = 0 // redefining a variable is only possible in the REPL zero: Int = 0

Long Integers

As already mentioned, integer literals of type Long are denoted when followed by a L suffix; and values of type Long are integer numbers between −263−263 and 263−1263−1, inclusive.

Scala REPL
scala> val zero = 0L  // type Long is detected by the compiler
zero: Long = 0 
scala> val zero: Long = 0L // It is better to explicitly state all types zero: Long = 0
scala>
val zero: Long = 0 // Automatic conversion from Int to Long zero: Long = 0
scala>
5 * zero // Multiplying an Int (5) with a Long (zero) returns a Long res2: Long = 0
scala>
val zero: Int = 0L <console>:11: error: type mismatch; found : Long(0L) required: Int val zero: Int = 0L ^ scala> val long3: Int = -42L // Types must match when explictly stated ^ error: type mismatch; found : Long(-42L) required: Int

Hex Integers

Hex integers are denoted with a leading 0x. Hex digits are always interpreted as Int.

Scala REPL
scala> val x = 0x7F
x: Int = 127
scala>
val x: Int = 0xff // Case insensitive; 0xff and 0xFF produce the same value x: Int = 255
scala>
val x: Int = 0x7F // Again, it is best to explicitly declare the types of all variables x: Int = 127

Two’s complement arithmetic is used, which means that a 32 bit value of all ones (0xffffffff) represents -1.

Scala REPL
scala> val x = 0xffffffff
x: Int = -1
scala>
val hex1 = 0xFFFF_FFFF // The optional underscore makes the hex literal easier to read hex1: Int = -1

Larger values may not be specified using literal hex integers, even when cast to Long.

Scala REPL
scala> val x = 0x7ffffffff
^
       error: integer number too large
scala>
val x: Long = 0x7ffffffff ^ error: integer number too large

Underscores

The ability to embed underscores anywhere in a numeric literal was introduced in Scala 2.13. As you will see throughout this course and the next, Scala has many unrelated uses for underscores.

Scala REPL
scala> 21_000
res3: Int = 21000
scala>
val x21_2: Int = 21_000 x21_2: Int = 21000
scala>
2_1_0_0_0 + 1 res4: Int = 21001
scala>
val x21_4: Int = 2_1_0_0_0 x21_4: Int = 21000

Floating Point

Floating point literals are of type Float when followed by a floating point type suffix F or f, and are of type Double otherwise. The type Float consists of all IEEE 754 32-bit single-precision binary floating point values, whereas the type Double consists of all IEEE 754 64-bit double-precision binary floating point values.

If a floating point literal in a program is followed by a token starting with a letter, there must be at least one intervening whitespace character between the two tokens.

0.0 1e30f 3.14159f 1.0e-100 .1

 – From Scala Specification
Scala REPL
scala> val x = 0.0  // The default floating point type is Double
x: Double = 0.0
scala>
val x: Double = 0.0 // Again, best to to declare all types x: Double = 0.0
scala>
val x = 1e30f // The f specifier indicates type Float x: Float = 1.0E30
scala>
val x: Double = 1e30f // Upcast from Float to Double by specifying the type x: Double = 1.0000000150474662E30
scala>
val x = 1.0e-100 x: Double = 1.0E-100

Boolean

Values for type Boolean are true and false.

Scala REPL
scala> val b = true
b: Boolean = true
scala>
val b = false b: Boolean = false
scala>
val b: Boolean = false // Again, it is best to explicitly declare the type of every variable b: Boolean = false

String

A string literal is a sequence of characters in double quotes. The characters can be any Unicode character except the double quote delimiter or \u000A (LF) or \u000D (CR); or any Unicode character represented by either a Unicode escape or by an escape sequence.

If the string literal contains a double quote character, it must be escaped using "\"".

The value of a string literal is an instance of class String.

Here are examples:

Shell
scala> "Hello,world!\n"
res0: String = "Hello, world!"
scala>
"\"Hello,\" replied the world." res1: String = ""Hello," replied the world."

Character

A character literal is a single character enclosed in quotes. The character can be any Unicode character except the single quote delimiter or \u000A (LF) or \u000D (CR); or any Unicode character represented by either a Unicode escape or by an escape sequence.

’a’ ’\u0041’ ’\n’ ’\t’

Note that although Unicode conversion is done early during parsing, so that Unicode characters are generally equivalent to their escaped expansion in the source text, literal parsing accepts arbitrary Unicode escapes, including the character literal ’\u000A’, which can also be written using the escape sequence ’\n’.

Some examples in the REPL:

Scala REPL
scala> val x = ’a’
x: Char = a
scala>
println(x) a
scala>
val x = ’\u0041’ x: Char = A
scala>
val x: Char = ’\u0041’ // Again, specify all types! x: Char = A

You can add Chars together to yield another Char. Sometimes this is what you didn’t mean, so take care:

Scala REPL
scala> println(’\u0041’ + ’\t’ + ’x’)  // Error: This does not concatenate 3 Chars into a String
194
scala>
println(’\u0041’.toString + ’\t’.toString + ’x’.toString) // This produces a String with 3 characters A x

Ranges

Let’s explore how to define a Range of integers containing values from 1 to three, inclusive. Note that the REPL stores the result in an automatically named variable called res0, of type scala.collection.immutable.Range.Inclusive:

Scala REPL
scala> 1 to 3
res0: scala.collection.immutable.Range.Inclusive = Range(1, 2, 3)

The range above was written with infix notation. This is possible because to is actually a method that takes a single parameter. Any method which takes a single parameter can be written with infix notation. We could have written the statement with dot notation to get the same effect:

Scala REPL
scala> 1.to(3)
res2: scala.collection.immutable.Range.Inclusive = Range(1, 2, 3) 

The above seems like the 1, an Int value object, has a method called to that accepts the argument 3. In fact, this is not actually true – instead, the Int type has been enriched with a variety of methods through the use of predefined implicits. The next course (Intermediate Scala) teaches you how to enrich classes through the use of implicits.

We could also do away with the syntactic sugar, and write the same range as follows. Note that this is not any more efficient, and is harder to read:

Scala REPL
scala> scala.collection.immutable.Range.inclusive(1, 3)
res3: scala.collection.immutable.Range.Inclusive = Range(1, 2, 3) 

We could shorten the previous incantation using an import statement to import the Range class.

Scala REPL
scala> import scala.collection.immutable.Range
import scala.collection.immutable.Range 
scala>
Range.inclusive(1, 3) res4: scala.collection.immutable.Range.Inclusive = Range(1, 2, 3)

We can also import the inclusive method from the Range class. You can use the tab key to complete package and variable names.

Scala REPL
scala> import scala.collection.immutable.Range.inclusive
import scala.collection.immutable.Range.inclusive 
scala>
inclusive(1, 3) res5: scala.collection.immutable.Range.Inclusive = Range(1, 2, 3)

... and we can import all methods from a class:

Scal REPL
scala> import scala.collection.immutable.Range._
import scala.collection.immutable.Range._ 

Now let’s use the range to print out "Hello, world!" three times:

Scal REPL
scala> 1 to 3 foreach { i => println("Hello, world!") }
Hello, world!
Hello, world!
Hello, world! 

Notice that the Scala expression parsed from left to right, so the range is computed before the foreach is executed. In other words, the above is equivalent to:

Scala REPL
scala> (1 to 3) foreach { i => println("Hello, world!") }
Hello, world!
Hello, world!
Hello, world! 

Again, the above was written with infix notation. We could rewrite it using postfix notation (note the period between the range and foreach).

Scala REPL
scala> (1 to 3).foreach { i => println("Hello, world!") }
Hello, world!
Hello, world!
Hello, world! 

The above is also equivalent to:

Scala REPL
scala> Range.inclusive(1, 3).foreach { i => println("Hello, world!") }
Hello, world!
Hello, world!
Hello, world! 

Because all the methods of Range were included, we could also write this as:

Scala REPL
scala> inclusive(1, 3).foreach { i => println("Hello, world!") }
Hello, world!
Hello, world!
Hello, world! 

Now let’s display the value of i for each iteration of the loop:

Scala REPL
scala> 1 to 3 foreach { i => println("Hello, world #" + i) }
Hello, world #1
Hello, world #2
Hello, world #3 

Here is a more convenient way of writing the same thing. The current instance of the val i is substituted for $i in the string because string interpolation is enabled for strings that are prefaced with the letter s.

Scala REPL
scala> 1 to 3 foreach { i => println(s"Hello, world #$i") }
Hello, world #1
Hello, world #2
Hello, world #3 

Ranges are lazily computed. We will discuss what that means in the Immutable Collections and Memoization in Depth lectures of the Intermediate Scala course.

until and the indices Idiom

The to operator iterates up to and including the last value. However, when you want to iterate over a collection using indices, which are zero-based, you need to use the exclusive Range returned by until instead of the inclusive Range returned by to.

Scala REPL
scala> 0 to 3
res6: scala.collection.immutable.Range.Inclusive = Range(0, 1, 2, 3)
scala>
0 until 3 res7: scala.collection.immutable.Range = Range(0, 1, 2)

Scala treats Strings as a collection of characters, and you can reference any character of a String by providing the index of the desired character within parentheses:

Scala REPL
scala> "abcdef"(3)
res8: Char = d
scala>
val string = "abcdef" string: String = abcdef
scala>
string(2) res9: Char = c

Let’s access each character in string and print it:

Scala REPL
scala> string.length
res10: Int = 6
scala>
0 until string.length res11: scala.collection.immutable.Range = Range(0, 1, 2, 3, 4, 5)
scala>
0 until string.length foreach { i => println(string(i)) } a b c d e f

Iterating over a collection using zero-based indices is such a common thing to write that there is a shorthand idiom, indices, which generates the same exclusive Range:

Scala REPL
scala> string.indices
res12: scala.collection.immutable.Range = Range(0, 1, 2, 3, 4, 5)
scala>
string.indices foreach { i => println(string(i)) } a b c d e f

As another example, let’s create an immutable List (we will discuss immutable Lists in detail in the Immutable Collections lecture of the Intermediate Scala course).

Scala REPL
scala> val list = List("a", "b", "c")
list: List[String] = List(a, b, c)
scala>
0 until list.length foreach { i => println(list(i)) } a b c

The indices idiom works just the same for a List as it does for String:

Scala REPL
scala> list.indices foreach { i => println(list(i)) }
a
b
c 

Other Scala Control Statements

Scala has three other control statements: while, do while and various types of for statements.

while

Scala code
while (condition) {
  expression(s)
}

For example, we can write the previous example as:

Scala code
var i = 1
while (i<=3) {
  println(s"Hello, world #$i")
  i = i + 1
}

Let’s try this in the REPL:

Scala REPL
scala> var i = 1
i: Int = 1
scala>
while (i<=3) { | println(s"Hello, world #$i") | i = i + 1 | } Hello, world #1 Hello, world #2 Hello, world #3

We could also add a semicolon after the println so the entire while statement can be expressed on a single line:

Scala REPL
scala> var i = 1
i: Int = 1
scala>
while (i<=3) { println(s"Hello, world #$i"); i = i + 1 } Hello, world #1 Hello, world #2 Hello, world #3

Scala assignment returns Unit

Scala assignment returns Unit, unlike other languages like Java, where assignment returns the value assigned. This means i = i + 1 cannot be used in a conditional expression:

Scala code
while ((i = i + 1) <=3)
  println("THIS CODE WILL NOT COMPILE")

do while

Scala code
do {
  expression(s)
} while (condition)

Here is the same example written using do while:

Scala code
var i = 1
do {
  println(s"Hello, world #$i")
  i = i + 1
} while (i<=3)

Again, we will try this in the REPL, adding a semicolon after the println so the entire while statement can be expressed on a single line:

Scala REPL
scala> var i = 1
i: Int = 1
scala>
do { println(s"Hello, world #$i"); i = i + 1 } while (i<=3) Hello, world #1 Hello, world #2 Hello, world #3

for

For loops look a bit like for comprehensions, introduced later in this course. The key difference is that for loops do not contain the yield keyword, which will not be discussed in this course. Formatting is optional; we could write this way:

Scala code
for (i <- 1 to 3)
  println(s"Hello, world #$i")

Let’s look at this in the REPL:

Scala REPL
scala> for (i <- 1 to 3)
     |    println(s"Hello, world #$i")
Hello, world #1
Hello, world #2
Hello, world #3 

Or we could put it all on one line:

Scala REPL
scala> for (i <- 1 to 3) println(s"Hello, world #$i")
Hello, world #1
Hello, world #2
Hello, world #3 

Exercise 2

Define a method that accepts a String message and an Int that indicates the number of times to print it. Test the method with a variety of input values.

Solution 2

Exercise 3

What gets printed out? Why?

Scala code
for (i <- 1 to 3) {
  var i = 2
  println(i)
}

Solution 3


* indicates a required field.

Please select the following to receive Mike Slinn’s newsletter:

You can unsubscribe at any time by clicking the link in the footer of emails.

Mike Slinn uses Mailchimp as his marketing platform. By clicking below to subscribe, you acknowledge that your information will be transferred to Mailchimp for processing. Learn more about Mailchimp’s privacy practices.