# 4. Scala in CS1¶

Warning

This section is still in draft form but is nearly complete in terms of examples, subject to editing. There might still be a few rough spots. Comments welcome to gkt@cs.luc.edu.

This is an elaboration of our Google presentation slides: http://goo.gl/Q68fA.

## 4.1. Motivating Scala in CS1¶

• Programming in the small
• Scripting Environment
• Libraries allow for interesting code (JVM)
• Static type checking
• Uniform syntax
• Everything is a method call
• Powerful collections

Our motivation is to have the best of both worlds:

• Concise script-ability of languages like Python and Ruby
• Static type checking in the small makes it (generally) easy to fix compilation errors

The emerging worksheet model makes it possible to give students many examples that just work and can be adapted to solve new problems.

## 4.2. Getting Started¶

Like many agile languages, Scala embraces the notion of being able to get started using a REPL (Read-Evaluate-Print-Loop), which allows for interactive execution and spontaneous feedback.

We’re assuming the reader can set up Scala and Java (needed to run Scala, a language that targets the JVM primarily). Once you have Scala and Java installed, you can open up an interactive session using the command line. (For those who don’t prefer the command line, especially on Windows, we recommend installing IntelliJ Community Edition and the Scala plug-in. This will allow you to get an interactive session as well.)

$scala-2.10 Welcome to Scala version 2.10.3 (Java HotSpot(TM) 64-Bit Server VM, Java 1.7.0_45). Type in expressions to have them evaluated. Type :help for more information. scala> println("Hello, World") Hello, World ### 4.2.2. Types - “Lack” of Primitives¶ scala> 1 res1: Int = 1 In the following, the user types 1. and then hits the <tab> key–known as tab completion–to show the available operations and methods available for an Int. scala> 1.<tab> % & * + - / > >= >> >>> ^ asInstanceOf isInstanceOf toByte toChar toDouble toFloat toInt toLong toShort toString unary_+ unary_- unary_^ | ### 4.2.3. Literals are mostly what you expect¶ Integers scala> val a = 95 a: Int = 95 scala> val b = -95 b: Int = -95 Octal and Hexadecimal scala> val c = 039 <console>:1: error: malformed integer number val c = 039 ^ scala> val c = 037 warning: there were 1 deprecation warning(s); re-run with -deprecation for details c: Int = 31 scala> val d = 0xff d: Int = 255 scala> val d = 0xffac d: Int = 65452 Double and Float scala> val e = 1.34e-7 e: Double = 1.34E-7 scala> val f = 1.34e-7f f: Float = 1.34E-7 ### 4.2.4. Statements vs. Expressions¶ Scala eschews statements as a general rule in favor of expressions (which produce values), owing to its functional heritage, which emphasizes the composition of functions to effect computation. In practice, however, many expressions, however, behave like statements as they produce Unit as a result. A notable consequence of this language decision is that some constructs are not provided in Scala, including the familiar break and continue (found in loops). ### 4.2.5. Everything is an Object¶ Scala is not designed in a vacuum. Its heritage is one shared with Java, even if its ideas have value above and beyond Java. Everything is achieved in Scala with objects, although you can do a tremendous amount of programming before ever writing a class per se. There are two consequences to this decision: • There are no primitive types per se. Every one of the literals discussed previously is an object. This eliminates the need to distinguish int from Integer (a major pain point for Java prior to boxing/unboxing support). • There are very few keywords in Scala. Even when you encounter something that you think is a keyword, it is often just a method call. A really good example is the concept of mapping (map) from functional programming, which is a method common to just about every Scala collection. Let’s look at a couple of examples of methods. Here is an example of how to see the methods available to an Int instance: scala> a.<tab> % & * + - / > >= >> >>> ^ asInstanceOf isInstanceOf toByte toChar toDouble toFloat toInt toLong toShort toString unary_+ unary_- unary_^ | Where you see <tab>, you can use a feature known as tab completion in the REPL to see the options available to value a. You’ll notice one thing immediately, especially if you are familiar with Java: operator overloading! That’s right, every operator is a method. Let’s use the + method: scala> a.+(3) res5: Int = 98 scala> a + 3 res6: Int = 98 Let’s convert an Int to a Float: scala> val b = a.toFloat b: Float = 95.0 You can also invoke methods like toFloat (which take no parameters) without using dots. We will take advantage of this syntax as part of good Scala style (in many of our examples). scala> val b = a.toFloat b: Float = 95.0 We’re going to look at more advanced objects later (Scala collections) but this should give you a taste of what is possible. ### 4.2.6. Tuples¶ A language feature that was popularized (but not invented) by the Python language are tuples. Tuples eliminate the need for unwanted uses of a class for grouping multiple values. Let’s take a quick look. scala> val t = (3, 4) t: (Int, Int) = (3,4) scala> val u = (3.0, 4.0) u: (Double, Double) = (3.0,4.0) scala> val v = (3.0, 4) v: (Double, Int) = (3.0,4) Notice that Scala infers the type of each one of these value definitions. When working with a tuple, you’re really working with an object. You can inspect the methods that are available using the tab completion method shown previously. scala> t. _1 _2 asInstanceOf canEqual copy isInstanceOf productArity productElement productIterator productPrefix swap toString scala> u. _1 _2 asInstanceOf canEqual copy isInstanceOf productArity productElement productIterator productPrefix swap toString scala> v. _1 _2 asInstanceOf canEqual copy isInstanceOf productArity productElement productIterator productPrefix swap toString You can inspect the components of a tuple by using the _1, _2, ... methods. scala> t._1 res9: Int = 3 scala> t._2 res10: Int = 4 You’ll obviously not want to use these names to refer to the components of a tuple. Using pattern matching, you can extract the components of a tuple and bind them to proper names. For example, if your tuple represents an (x, y) pair, you are likely to use a match expression like this: scala> t match { case (x, y) => println(s"($x, $y)") } (3, 4) ### 4.2.7. Semicolon Inferencing¶ Python programmers already know and love not having to deal with semicolons. Scala follows this excellent practice as well. You can do this: scala> val x = 30; x: Int = 30 But this works just as well and is the preferred way to write Scala code: scala> val y = 30 y: Int = 30 ### 4.2.8. Simple Input (and Output)¶ Much like Python and Java, import can be used to experiment with library objects and functions, even before you know how to create classes: scala> import scala.tools.jline.console.ConsoleReader scala> val input = new ConsoleReader input: scala.tools.jline.console.ConsoleReader = scala.tools.jline.console.ConsoleReader@3ec642e5 Then you can inspect the capabilities of the ConsoleReader by using tab completion (as shown before) scala> input.r<tab> readCharacter readLine readVirtualKey redrawLine removeCompleter replace resetPromptLine restoreLine Now we look up more details on the readLine() methods, which is what we want to do basic, line-oriented input (a common need in introductory teaching). scala> input.readLine<tab> def readLine(): String def readLine(Character): String def readLine(String): String def readLine(String, Character): String scala> val data = input.readLine("Please enter some text: ") Please enter some text: Hello, World data: String = Hello, World You’ll probably find it necessary to read through the Scala documentation, but in a number of cases, the behavior is similar to what you’ve seen in other language APIs. readLine() is pretty well known in Java circles. As you can see above, readLine(String) gives us what we want: the ability to read input with a prompt of sorts. ### 4.2.9. val vs. var¶ • Values (keyboard val) are used for immutable storage. • Variables (keyword var) are used for mutable storage. • You can think of this as the reemergence of const but it takes on a more powerful and predictable form in Scala than other languages that preceded it. • Scala thinking prefers val to var. So do we. • Interesting ### 4.2.10. Scripts and Worksheets¶ Similar to modern scripting languages (e.g. Python and Ruby) and the original shell, you can create a Scala script in a file, e.g. myscript.scala and run the script using the scala command.$ scala myscript.scala

You can also load the script within the Scala REPL:

The sum is 5050

### 4.6.3. for comprehension¶

A for comprehension is designed for where you want a more functional style. That is, there is no intention of having side effects, and it is likely that you want to use the result of the comprehension in another computation.

Let’s look at something a bit more interesting: Getting the first n squares:

scala> val n = 10
n: Int = 10

scala> val first_n_squares = for (i <- 1 to n) yield { i * i }
first_n_squares: scala.collection.immutable.IndexedSeq[Int] =
Vector(1, 4, 9, 16, 25, 36, 49, 64, 81, 100)

You could wrap this up nicely in a Scala function as follows:

scala> def squares(n : Int) = for (i <- 1 to n) yield { i * i }
squares: (n: Int)scala.collection.immutable.IndexedSeq[Int]

scala> squares(10)
res7: scala.collection.immutable.IndexedSeq[Int] =
Vector(1, 4, 9, 16, 25, 36, 49, 64, 81, 100)

### 4.6.4. options and failure sans exceptions¶

Per the Scala documentation: An option represents optional values. Instances of Option are either an instance of scala.Some or the object None.

The Scala Option type is useful for dealing with the notion of success and failure (but is not limited to supporting this concept). In the interests of keeping this discussion focused on being CS1-friendly, an Option is always connected to an underlying type. For example Option[Int] means that you either get the Int, or nothing (None).

scala> val i = Some(3)
i: Some[Int] = Some(3)

scala> val j = None
j: None.type = None

Some and None are both case classes that extend Option[A] (a generic class that allows you to use any time). While knowing all of the details requires a mastery of object-oriented programming (and perhaps more), the introduction of this idea is remarkably straightforward and one that can help students to write better code from the beginning. In CS1 courses, we tend to spend a lot of time handling special cases, and options give us a framework for dealing with missing information, etc.

As you can see from the above, i is defined as some value, in this case 3. It’s a wrapped value, of course, that must be unbundled later. For example, here is an attempt to use the value i (incorrectly):

scala> i + 5
<console>:9: error: type mismatch;
found   : Int(5)
required: String
i + 5
^

So how do we make use of i and j? A method associated with options, getOrElse allows us to get the option, if set to some value. Rather delightfully, we can set a value to be used, if the option was not set previously.

scala> i getOrElse(-1)
res13: Int = 3

scala> j getOrElse(-1)
res14: Int = -1

So now if we want to use this value in a computation, we can just do this:

scala> i.getOrElse(-1) + j.getOrElse(-1)
res17: Int = 2

Options have their uses throughout Scala, notably its libraries. For example, maps (a.k.a. associative structures/arrays) use option to return the result of getting a key from the map:

scala> val map = scala.collection.mutable.HashMap.empty[String, Int]
map: scala.collection.mutable.HashMap[String,Int] = Map()

scala> map += ("scala" -> 10)
res4: map.type = Map(scala -> 10)

scala> map += ("java" -> 20)
res5: map.type = Map(scala -> 10, java -> 20)

scala> map += ("C#" -> 15)
res6: map.type = Map(scala -> 10, java -> 20, C# -> 15)

scala> map += ("F#" -> 25)
res7: map.type = Map(scala -> 10, F# -> 25, java -> 20, C# -> 15)

scala> map += ("scala" -> 22)
res8: map.type = Map(scala -> 22, F# -> 25, java -> 20, C# -> 15)

Here’s an attempt to get the case-sensitive and case-insensitive versions of key, F#, from the map:

scala> map.get("F#")
res9: Option[Int] = Some(25)

scala> map.get("f#")
res10: Option[Int] = None

Seasoned Java programmers know that an entry not found in the map will result in the null value being returned. This differs from Scala, because the value gotten from the map must be tested before attempting to use it in any way.

In Scala, because None and Some(25) are both options, you can use getOrElse to obtain the options value (irrespective of whether the value is null, or not set) without writing an (unwanted) if-then-else statement, which results in bloat (in most programming languages).

scala> val entry = map.get("F#").getOrElse(-1)
entry: Int = 25

scala> println(s"The entry for F# is $entry") The entry for F# is 25 It’s often the case that we have default values associated with failure to accomplish a certain task. The Option idiom is an attempt to standardize this for core data structures and (as we’ll see) other situations (e.g. working with complex for comprehensions). In the case of maps, an entry not found would usually default to 0 or -1 (a convention that dates back to the earliest days of C), which is preferable to throwing exceptions for no good reason (not to mention our general dislike of prematurely covering exceptions as a programming technique in CS1 in particular.) ### 4.6.5. yield¶ Yield is used to take a collection and produce a new one with mapped values. Let’s produce a List[Int] of squares from a List[Int] of the first three integers: scala> val l = List(1, 2, 3) l: List[Int] = List(1, 2, 3) scala> l res10: List[Int] = List(1, 2, 3) scala> for (i <- l) yield { i * i } res11: List[Int] = List(1, 4, 9) Let’s try this with an Array[Int]: scala> val a = Array(1, 2, 3) a: Array[Int] = Array(1, 2, 3) scala> for (i <- a) yield i * i res16: Array[Int] = Array(1, 4, 9) For the most part, when iterating over values and using yield, you will always get back the same type, or another type that makes sense. In these basic examples, the above can also be written as follows (without the for): scala> l map (i => i * i) res17: List[Int] = List(1, 4, 9) scala> a map (i => i * i) res18: Array[Int] = Array(1, 4, 9) ### 4.6.6. Ranges¶ Ranges should be familiar to you if you’ve worked with other agile scripting languages, e.g. Python. scala> Range(1, 5) res20: scala.collection.immutable.Range = Range(1, 2, 3, 4) This gives a range of values from 1 to 5 but stopping at the last value before 5. The increment is +1. scala> Range(1, 9, 2) res22: scala.collection.immutable.Range = Range(1, 3, 5, 7) You can also work backwards: scala> Range(9, 0, -2) res24: scala.collection.immutable.Range = Range(9, 7, 5, 3, 1) ### 4.6.7. Multiple generators¶ ### 4.6.8. If guards¶ ### 4.6.9. Variables¶ ### 4.6.10. Patterns¶ ## 4.7. Files¶ • Can use Scanner • scala.io.Source • Scala Iterator[Char] • getLines : Iterator[String] • Use with higher-order methods • Write with PrintWriter • Introduce APIs? ## 4.8. Classes and Objects¶ ### 4.8.1. Classes¶ Here is the familiar Point class. It’s often shown where the (x, y) coordinate pair are Int (even in the Scala documentation) but is even more interesting with Double. This is an elaborated version that includes elements appropriate mostly to CS1 and some that are best covered in CS2 and beyond.  1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 class Point(initial_x: Double, initial_y: Double) { var x: Double = initial_x var y: Double = initial_y def move(dx: Double, dy: Double) { x = x + dx y = y + dy } def distanceToOrigin() : Double = { math.sqrt(x * x + y * y) } def distanceTo(p : Point) = { math.sqrt( (p.x - x) * (p.x - x) + (p.y - y) * (p.y - y) ) } def add(p : Point) = { x = x + p.x y = y + p.y this } def +(p : Point) = { add(p) } override def toString(): String = s"($x, $y)"; } What does this class Point show? • how to create a simple Scala class. Notice the complete lack of keyboards and public/private/static that tend to confuse students! • Scala brings back disciplined operator overloading. We’d probably not use this in CS1, but it is entirely appropriate for CS2. • Shows how to refer to the object (familiarly) with this. Again, the methods relying on this might be more appropriate for CS2 or even later courses that dive into OOP’s complexity. • Constructors? The Scala class definition itself makes it clear how one constructs an instance. Just like a function definition in general, there can be default values. It is liberating not to have constructors (especially too many of them), especially when trying to introduce a topic. (This will become readily apparent when we look at case classes, which provide a mechanism for more data-centric OO abstraction.) • Convert an instance of class Point to a String representation using ToString(). ToString() can be a valuable pedagogical tool, done right (as observed in languages like Python). Scala 2.10 gives us the ability to do type-safe string interpolation by substituting the value of variables (their String representation) into a String template. In Scala, prefixing a string literal with s will give you a string where any variables (beginning with$) are substituted. Here, it allows us to get a beautiful representation of a point as an (x, y) pair with virtually no effort or complexity!

Let’s take a look at how the Point class is used:

 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 val p = new Point(2, 3) val q = new Point(-2, 3) println(s"Two points $p and$q") val distanceToOrigin = p.distanceToOrigin() println(s"distance from p to origin = $distanceToOrigin") val dpq = p.distanceTo(q) val dqp = q.distanceTo(p) println(s"d(p,q) =$dpq") println(s"d(q,p) = $dqp") val pointSum = p.add(q) val pointSumOp = p + q println(s"p.add(q) =$pointSum; p + q = $pointSumOp") This results in the following output. scala> :load point.sc :load point.sc Loading point.sc... defined class Point p: Point = (2.0, 3.0) q: Point = (-2.0, 3.0) Two points (2.0, 3.0) and (-2.0, 3.0) distanceToOrigin: Double = 3.605551275463989 distance from p to origin = 3.605551275463989 dpq: Double = 4.0 dqp: Double = 4.0 d(p,q) = 4.0 d(q,p) = 4.0 pointSum: Point = (0.0, 6.0) pointSumOp: Point = (-2.0, 9.0) p.add(q) = (-2.0, 9.0); p + q = (-2.0, 9.0) ### 4.8.2. A Look at Singleton Objects¶ At some point, relying on the interpreter for trying out the Point class (as we have been doing until now) grows a bit tedious. Furthermore, sometimes you want to have a complete, functioning program that includes not just the class Point but also a driver program–often found in a main() method–that allows you to run it from the command line. Scala’s answer to main() (largely a vestige of C-based languages) is to support singleton objects, which we rely upon in some more advanced examples. While we consider the singleton pattern to be a bit overrated and overused (e.g. Runtime.getRuntime() and many others like it in Java’s API), the singleton object as found here is completely decoupled from any class and allows you to create an environment so to speak with its own namespace but without the burden of a full class definition. In this example, we create a singleton object to act as our main() driver.  1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 object PointDemo { def apply() { val p = new Point(2, 3) val q = new Point(-2, 3) println(s"Two points$p and $q") val distanceToOrigin = p.distanceToOrigin() println(s"distance from p to origin =$distanceToOrigin") val dpq = p.distanceTo(q) val dqp = q.distanceTo(p) println(s"d(p,q) = $dpq") println(s"d(q,p) =$dqp") val pointSum = p.add(q) val pointSumOp = p + q println(s"p.add(q) = $pointSum; p + q =$pointSumOp") } } PointDemo()

Similar to a class definition, you can have methods. Notably absent, however, are any parameters to the object name. To create an entry point that allows the object to be used like any other Scala function, we define an apply() method, which may or may not have parameters. In our case, we just want to be able to run the same demo code we produced previously and then invoke the PointDemo as a function, e.g. PointDemo(). This could be further wrapped with some logic to handle command-line arguments, etc. More on that towards the end.

## 4.9. Useful REPL functionality¶

The Scala REPL supports a number of commands that can be greatly helpful for working interactively. We’ve relied on many of these in the preparation of this tutorial but will focus on the highlights, especially for use in CS1 teaching.

scala> :help
All commands can be abbreviated, e.g. :he instead of :help.
Those marked with a * have more detailed help, e.g. :help imports.

:cp <path>                 add a jar or directory to the classpath
: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
:paste                     enter paste mode: all input up to ctrl-D compiled together
:power                     enable power user mode
:quit                      exit the interpreter
:replay                    reset execution and replay all previous commands
:reset                     reset the repl to its initial state, forgetting all session entries
:sh <command line>         run a shell command (result is implicitly => List[String])
:silent                    disable/enable automatic printing of results
:type [-v] <expr>          display the type of an expression without evaluating it
:warnings                  show the suppressed warnings from the most recent line which had any

### 4.9.1. paste¶

When writing longer definitions in the REPL, it can be tricky. Having paste mode allows you to take some code you have (perhaps from an editor where you are typing a Scala program) and copy/paste into the Scala session.

This shows an example of entering a slightly more verbose than needed definition of the square() function (presented earlier in this section):

scala> :paste
// Entering paste mode (ctrl-D to finish)

def square(x : Int) : Int = {
x * x
}

// Exiting paste mode, now interpreting.

square: (x: Int)Int

Notice that you don’t see the continuation characters when entering multiple lines of text.

Many programmers coming to Scala find it a bit frustrating at first that some things (like interactive scripts, found in languages like Python) don’t work quite the same way. More often than not, the real issue is whether there is an easy way to load a script into the REPL–as opposed to having to run it on the command line (which is also possible but not the focus of this section). It is a matter of loading the filename, which may be an absolute or relative path.

### 4.9.3. history¶

History should be familiar to anyone who has used modern Unix shells. Even if you haven’t, you’ve probably used the history buffer, which allows you to use the up/down arrows or emacs/vi commands (^p, ^n, j, k) to access previous and next commands in the history buffer.

Scala also allows you to do ^r to perform a regex search for text within a previous command. We rely on this heavily in our work, especially in this section, where it was necessary to look up previous attempts within the REPL so they could be pasted into the notes!

In this example, I used ^r to search for the substring “val” so I could find a previous value definition in my REPL session:

(reverse-i-search)`val': val entry = map.get("F#").getOrElse(-1)

When you type ^r, you’ll be given the “(reverse-i-search)” prompt to perform a search. While the full functionality of regex is provided, the nominal use is to type a few characters of something you probably remember (at least partially). More often than not, I am looking for previous “val” or “def” (functions).