Mike Slinn

Learning Scala Using The REPL 1/3

— Draft —

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

This lecture is a first look at some simple Scala programs and how to run them using the Scala REPL. Variable definitions, method definitions, Scala / Java interoperability, the runtime fidelity if the REPL, expressions, and pasting transcripts are discussed. An exercise is also provided.

REPL stands for: read, evaluate, print loop. In other words, a REPL is an interpreter. Python, Ruby and Scala all have REPLs.

The Scala REPL provides 95% fidelity of the Scala runtime environment. There are some important differences, however, and I will point them out as we go along. You can try out the Scala REPL by typing scala at a shell prompt:

The Scala 2 REPL and the Scala 3 REPL work differently in many ways.

I set JAVA_OPTS as described in the Illegal Reflective Access section of the Installing Scala lecture. You may need to do this as well to run the Scala REPL.

REPL Help Messages

Scala 2 REPL help message
$ scala
Welcome to Scala 2.13.0 (OpenJDK 64-Bit Server VM, Java 11.0.3).
Type in expressions for evaluation. Or try :help. 
scala> :help All commands can be abbreviated, e.g., :he instead of :help. :edit <id>|<line> edit history :help [command] print this summary or command-specific help :history [num] show the history (optional num is commands to show) :h? <string> search the history :imports [name name ...] show import history, identifying sources of names :implicits [-v] show the implicits in scope :javap <path|class> disassemble a file or class name :line <id>|<line> place line(s) at the end of history :load <path> interpret lines in a file :paste [-raw] [path] enter paste mode or paste a file :power enable power user mode :quit exit the interpreter :replay [options] reset the repl and replay all previous commands :require <path> add a jar to the classpath :reset [options] reset the repl to its initial state, forgetting all session entries :save <path> save replayable session to a file :sh <command line> run a shell command (result is implicitly => List[String]) :settings <options> update compiler options, if possible; see reset :silent disable/enable automatic printing of results :type [-v] <expr> display the type of an expression without evaluating it :kind [-v] <expr> display the kind of expression’s type :warnings show the suppressed warnings from the most recent line which had any

Scala 3 removed most of the REPL commands:

Scala 3 REPL help message
$ scala
$ Welcome to Scala 3.4.2 (11.0.23, Java OpenJDK 64-Bit Server VM).
Type in expressions for evaluation. Or try :help. 
scala> :help The REPL has several commands available:
:help print this summary :load interpret lines in a file :quit exit the interpreter :type evaluate the type of the given expression :doc print the documentation for the given expression :imports show import history :reset [options] reset the repl to its initial state, forgetting all session entries :settings update compiler options, if possible

Using the REPL

Let’s output the obligatory “Hello, world!” message:

Scala REPL demo
scala> println("Hello, world!")
Hello, world!  

We can define an immutable variable called x by using val:

Scala REPL demo (continued)
scala> val x = 3
x: Int = 3 

After you typed the above Scala expression and pressed Enter, the Scala REPL displayed the name of the variable, followed by its type and then its value.

You can also explicitly define the type of the variable, if you are concerned that someone reading your code might be uncertain about the type that might be inferred:

Scala REPL demo (continued)
scala> val x: Int = 3
x: Int = 3 

If a variable’s type does not match the type of an assingment value, a compile-time error will occur. Scala 2 displays the error like this:

Scala REPL demo (continued)
scala> val x: Int = "three"
<console>:7: error: type mismatch;
 found   : String("three")
 required: Int
       val x: Int = "three" 

Scala 3 displays the error like this:

Shell
$ scala
Welcome to Scala 3.4.2 (11.0.23, Java OpenJDK 64-Bit Server VM).
Type in expressions for evaluation. Or try :help.
scala>
val x: Int = "three" -- [E007] Type Mismatch Error: ------------------------------------------------- 1 |val x: Int = "three" | ^^^^^^^ | Found: ("three" : String) | Required: Int | | longer explanation available when compiling with `-explain` 1 error found

Scala 3 with -explain displays the error like this:

Scala REPL
$ scala -explain
Welcome to Scala 3.4.2 (11.0.23, Java OpenJDK 64-Bit Server VM).
Type in expressions for evaluation. Or try :help.
scala>
val x: Int = "three" -- [E007] Type Mismatch Error: ------------------------------------------------- 1 |val x: Int = "three" | ^^^^^^^ | Found: ("three" : String) | Required: Int |----------------------------------------------------------------------------- | Explanation (enabled by `-explain`) |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | | Tree: "three" | I tried to show that | ("three" : String) | conforms to | Int | but none of the attempts shown below succeeded: | | ==> ("three" : String) <: Int | ==> String <: Int = false | | The tests were made under the empty constraint ----------------------------------------------------------------------------- 1 error found

Notice that Int is spelled with an upper case I:

Scala REPL demo (continued)
scala> val x: int = 3
<console>:7: error: not found: type int
       val x:int = 3 

Mutable Variables

We can define a mutable variable using var (instead of val), then change its value:

Scala REPL demo (continued)
scala> var y = 4
y: Int = 4 
scala> y = 36 y: Int = 36

Of course, we cannot change the value of an immutable variable. Scala 2 displays the following error message:

Scala REPL demo (continued)
scala> x = 4
<console>:8: error: reassignment to val
       x = 4
         ^ 

Scala 3 displays the error like this:

Scala REPL demo (continued)
scala> x = 4
-- [E052] Type Error: ----------------------------------------------------------
1 |x = 4
  |^^^^^
  |Reassignment to val x
  |
  | longer explanation available when compiling with `-explain`
1 error found 

Lets use the -explain option for Scala 3:

Shell
$ scala -explain
Welcome to Scala 3.4.2 (11.0.23, Java OpenJDK 64-Bit Server VM).
Type in expressions for evaluation. Or try :help.
scala>
val x: Int = 3 val x: Int = 3
scala>
x = 4 -- [E052] Type Error: ---------------------------------------------------------- 1 |x = 4 |^^^^^ |Reassignment to val x |----------------------------------------------------------------------------- | Explanation (enabled by `-explain`) |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | You can not assign a new value to x as values can't be changed. | Keep in mind that every statement has a value, so you may e.g. use | val x = if (condition) 2 else 5 | In case you need a reassignable name, you can declare it as | variable | var x = ... ----------------------------------------------------------------------------- 1 error found

Methods

We can define a method and invoke it in the REPL. The type of argument x is defined to be Int, and the implementation of the method follows the equals sign.

Defining and calling a method in the Scala REPL
scala> def timesTwo(x: Int) = 2 * x
timesTwo: (x: Int)Int 
scala> timesTwo(21) res6: Int = 42

The Scala compiler inferred the return type of the method definition above as Int. It is generally a good habit to declare variable types and the return type of methods, unless it is really obvious to a person reading your code:

Defining the return type of a method as Int
scala> def timesTwo(x: Int): Int = 2 * x
timesTwo: (x: Int)Int 

If we declare the return type as Double then the result will be converted from an Int to a Double:

Converting a return type automatically
scala> def timesTwo(x: Int): Double = 2 * x
timesTwo: (x: Int)Double 
scala> timesTwo(21) res7: Double = 42.0

Method Syntaxes

Methods can be defined within objects, classes and traits. When you define a method at the REPL prompt, the new definition is added to an invisible wrapper class. This section describes the various ways of when defining Scala methods.

If you only defined the signature of a method, and have not yet written the implementation (also known as the body), but you want the code to compile, just write ??? to act as a placeholder for the body of the method. The following method accepts two parameters (a String and an Int), and returns a String:

Defining a method signature without providing an implementation yet
def doSomething(a: String, b: Int): String = ???

If the method has a side effect (in other words, if it alters the program state), but does not return a computable value, write it with empty parentheses and return Unit:

Marking a Scala method as performing a side effect
def performSideEffect(): Unit = ???

Compare the above with the syntax used for a method that returns no value and has no side-effects. Obviously, if a method has no side effect and returns no computable value then it does not perform any useful work, so you should never see this syntax in a properly-written program:

Bad! Do not write this way
def suspectMethod: Unit = ???

If a method has no side-effects, does not accept parameters and simply returns a value, it is termed referentially transparent. Write it this way, without parentheses and declare the type returned. In this case, the method returns an instance of X:

Good, referentially transparent
def doSomething: X = ???

It is possible to use Scala’s type inference to figure out what the return type is. In this example, the return type is not declared. I do not recommend you get in the habit of writing methods this way, because this allows an entire class of programming errors to creep into your code.

Bad: no return type specified
def doSomething() = ???

Scala / Java Interoperability

Java to Scala

Scala does not define a String class; instead, it uses java.lang.String. We can create a String in Scala just the same as we would in Java:

Scala REPL
scala> "hello "
res8: String = "hello " 

Scala’s Predef implicitly enriches Java Strings as required, so Strings appear to have extra capabilities. Some of these capabilities are defined in scala.StringContext and two classes in the scala.collection.immutable package: WrappedString and StringOps. Here is an example of a Java String used in a Scala program:

Scala REPL
scala> "hello " * 3
res9: String = "hello hello hello " 

Scala’s Predef also implicitly converts Java ints and Integers into Scala Ints as required:

Scala REPL
scala> val i = new java.lang.Integer(123)
i: Integer = 123 
scala> i * 3 res10: Int = 369

All of Java’s numeric types are implicitly converted to Scala equivalents as required. The Parametric Types lecture of the Intermediate Scala course has more detail. We will need to cover a lot of material before the Predef magic makes sense, so that’s why the lecture on Predef is not presented in this course.

Scala to Java

Scala’s Predef implicitly converts Scala objects into their Java equivalents as required. The Java method System.out.println() only works with Java values and objects, so I will use it to demonstrate. I’ll pass in a Scala Int, and it will automatically be converted to a Java int.

Scala REPL
scala> System.out.println(i)
123 

95% Runtime Fidelity

One of the biggest differences between the Scala REPL and the Scala compiler is that the Scala compiler does not allow you to redefine the type of a variable or method once it is defined.

The Scala REPL actually creates a new runtime context each time you issue a statement. As you experiment with the Scala REPL, realize that definitions might behave differently than they would when compiling a real Scala program.

Here is another difference: variables and methods are always associated with an instance of a class or an object. We’ll talk about objects more a bit later, but for now just know that they are singleton instances of a class. You can define an object like this:

Defining a Scala object
object Blah {
  val x = 3
  def timesTwo(i: Int) = 2 * i
}

For the REPL, you can paste in all of the lines together, like this:

Scala REPL
scala> object Blah {
     |   val x = 3
     |   def timesTwo(i: Int) = 2 * i
     | }
// defined object Blah 

Notice that the REPL printed vertical bars between the opening and closing braces to indicate that the expression I typed continued from line to line. We could also use a semicolon to separate the statements if they are written on the same line, like this:

Defining an object in the Scala REPL
scala> object Blah { val x = 3; def timesTwo(i: Int) = 2 * i }
// defined object Blah 

Unknown to us, the Scala REPL wrapped the variables and methods we defined earlier in this lecture in an object. We don’t have access to the object itself, just its properties and methods. You do have access to the Blah object we just created, however:

Accessing object properties in the Scala REPL
scala> Blah.x
res8: Int = 3 
scala> Blah.timesTwo(21) res9: Int = 42

Exercise 1

Define a method that squares any Int passed to it, and test it on a variety of input values.

Solution 1

Shell
$ scala
Welcome to Scala 2.13.14 (OpenJDK 64-Bit Server VM, Java 17.0.11).
Type in expressions for evaluation. Or try :help.
scala>
def square(i: Int) = i * i def square(i: Int): Int
scala>
square(3) val res0: Int = 9
scala>
square(-5) val res1: Int = 25

Everything is an Expression

An expression is a combination of values and functions that are combined and interpreted by the compiler to create a new value, as opposed to a statement which is just a standalone unit of execution and doesn’t return anything. One way to think of this is that the purpose of an expression is to create a value (with some possible side-effects), while the sole purpose of a statement is to have side effects. In Scala there are many more expressions than statements.

In Scala, expressions return a value, while statements return Unit, which is a marker that indicates no value is returned. Another way of saying this is that everything in Scala is an expression; statements are expressions that return Unit.

A block of code is an expression, and the last expression in the block establishes the return value. For example, here is a statement that computes the value of Pi/2 (90 degrees, expressed in radians):

Scala REPL
scala> math.Pi / 2.0
res0: Double = 1.5707963267948966 

The REPL stored the value returned by the computation in a new variable it created called res0 and displayed the value on the console.

We can store the result of the expression in a variable with a name of our choosing:

Scala REPL
scala> val ninety = math.Pi / 2.0
ninety: Double = 1.5707963267948966 

Here is a method definition that encapsulates the computation. This method is a bit unusual in that it does not accept any parameters, but that is valid. The computed value of the expression math.PI / 2.0 becomes the value returned by the method.

Scala REPL
scala> def piBy2: Double = math.Pi / 2.0
piBy2: Double 

When we call the piBy2 method the REPL stores the value returned by the method into a new variable called res1 and displays the value on the console:

Scala REPL
scala> piBy2
res1: Double = 1.5707963267948966 

Again, we can store the returned value in a variable name of our choosing:

Scala REPL
scala> val zzpluralz = piBy2
zzpluralz: Double = 1.5707963267948966 

Only the value of the last expression in a block of code is returned.

String Interpolation

We can insert a println in a block of code. This technique is often used for debugging.

Scala method definition
def piBy2: Double = {
  val result = math.Pi / 2.0
  println(s"result=$result")
  result
}

The above method has 3 lines:

  1. Declares an immutable variable called result and compute its value and type.
  2. Computes an interpolated string value and output it.
  3. The value returned by the block is the value of the last expression that was evaluated for that block, which is the value of the result variable.

If we run the above by pasting the code into the Scala REPL, then typing the name of the method, we get the following:

Scala REPL
scala> piBy2
result=1.5707963267948966
res3: Double = 1.5707963267948966 

Note the expression s"result=$result" This is called string interpolation, and it allows you to easily create Strings using variable values.

  • Prepending s to any string literal allows the usage of variables directly in the string
  • Each variable reference is prefixed by a dollar sign ($).
  • You can enclose computations and chained method calls within curly braces following a dollar sign, such as s"result = ${ 1 + result }"
  • Within curly braces, spaces are optional, and semicolons can be used to separate expressions

In addition to s interpolation, there are other kinds of string interpolations, including f for formatting, and raw for raw strings.

Even Scala’s if-then-else construct is an expression. The value returned from a Scala if-then-else expression is commonly used to set a variable or as a method’s return value. For example, let’s create two variables, x and y, and return the larger of the two:

Scala REPL
scala> val x = 13
x: Int = 13 
scala> val y = 14 y: Int = 14
scala> if (x>y) x else y res4: Int = 14

Notice that the value of the else clause became the value of the entire if expression. We can wrap this computation in a method called intMax. The following performs the same function as math.max, except that intMax is limited to working with Int arguments:

Scala REPL
scala> def intMax(x: Int, y: Int) = if (x>y) x else y
intMax: (x: Int, y: Int)Int 
scala> intMax(13, 14) res5: Int = 14

The compiler is smart enough to know that the value returned by bigger must be an Int, but you can declare the return type if you wish to avoid bugs that tend to creep in when no-one is watching:

Scala REPL
def intMax(x: Int, y: Int): Int = if (x>y) x else y

Pasting a Transcript

The REPL can accept a previously transcribed REPL session, parse out the original text entered by the user and replay the original text. This is handy when typing along with a ScalaCourses lecture.

All you have to do is select the original text of an SBT REPL session from a web page or other text source, copy the entire text, including the scala> prompts and REPL responses, move to the Scala REPL, paste in the text and press Ctrl-D. The original text will then be replayed, and new variables will be allocated.

The copy action varies depending on the OS and the program that you are copying from: usually Ctrl-C or Cmd-C will copy the currently selected text to the operating system’s paste buffer, but some programs use Ctrl-Shift-C or Ctrl-Ins and for other programs such as X-Windows and ConEmu, merely selecting text causes it to be copied to the paste buffer.

Let’s try this for the REPL session we just looked at. After copying the text shown above into the system paste buffer, paste it into the REPL window.

Again, the paste action varies depending on the program that you are pasting into: usually Ctrl-V or Cmd-V will paste the contents of the operating system’s paste buffer into the currently active window, but some programs use Shift-Ins and for other programs such as X-Windows and ConEmu, a right-click with the mouse performs the paste action.

Once you have pasted a copy of the transcript into the REPL, press Ctrl-D (shown as ^D).

Scala REPL
scala> val x = 13
// Detected repl transcript paste: ctrl-D to finish.
x: Int = 13
scala> val y = 14 y: Int = 14
scala> if (x>y) x else y res13: Int = 14 ^D // Replaying 3 commands from transcript.
scala> val x = 13 x: Int = 13
scala> val y = 14 y: Int = 14
scala> if (x>y) x else y res0: Int = 14

The original REPL transcript created variables called x and y, and the replayed transcript does the same. Notice that the last line allocates a variable called res0, even though the original transcript’s allocated res13. This is because the current REPL session’s automatic variable assignments are independent of the original REPL transcript’s automatic variable assignments, even when pasting in a transcript.


* 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.