Creative Scala

Dave Gurnell and Noel Welsh

Foreword

Creative Scala is aimed at developers who have no prior experience in Scala. It is designed to give you a fun introduction to functional programming. We assume you have some familiarity with another programming language but little or no experience with Scala or other functional languages.

Our goal is to demonstrate the building blocks that Scala developers use to create programs in a clear, succinct, declarative manner. Working through the exercises in the book should take a few hours, after which we hope you will have a feel of what Scala can do for your applications.

1 Expressions, Values, and Types

Scala programs have three fundamental building blocks: expressions, values, and types. An expression is a fragment of Scala code that we write in an a text editor. Valid expressions have a type and calculate a value. For example, this expression has the type String and calculates the value HELLO WORLD!

"Hello world!".toUpperCase
// res0: String = "HELLO WORLD!"

A Scala program goes through two distinct stages. First it is compiled; if compiles successfully it can then be executed. The most important distinction between types and values is that types are determined at compile time, whereas values can only be determined at run time. Values can change each time we run the code, whereas types are fixed. For example, the following expression is certainly of type String, but its value depends on the the user input each time it is run:

readLine.toUpperCase

We are used to thinking of types that refer to sets of literals such as Int, Boolean, and String, but in general a type is defined as anything we can infer about a program without running it. Scala developer use types to gain assurances about our programs before we put them into production.

1.1 Simple Scala Expressions

Let’s look at some of the basic kinds of expressions we can write in Scala:

1.1.1 Literals

The simplest kinds of expressions are literals. These are fragments of code that “stand for themselves”. Scala supports a similar set of literals to Java:

// Integers:
1
// res0: Int = 1

// Floating point numbers:
0.1
// res1: Double = 0.1

// Booleans:
true
// res2: Boolean = true

// Strings:
"Hello world!"
// res3: String = Hello world!

// And so on...

1.1.2 Method Calls

Scala is a completely object-oriented programming language, so all values are objects with methods that we can call. Method calls are another type of expression:

123.4.ceil
// res4: Double = 124.0

true.toString
// res5: String = "true"

1.1.3 Constructor Calls

Scala only has literals for a small set of types (Int, Boolean, String, and so on). To create values of other types we either need to call a method, or we need to call a constructor using the new keyword. This behaves similarly to new in Java:

import java.util.Date
// import java.util.Date

new Date()
// res4: java.util.Date = Tue Feb 10 10:30:21 GMT 2015

The new operator tends to be a distraction when writing larger, more complex expressions. For this reason, Scala libraries typically provide factory methods to wrap constructor calls. The effective difference is that we can create many Scala data types without writing new:

List(1, 2, 3)
// res6: List[Int] = List(1, 2, 3)

Other than the lack of a new keyword, the semantics here are similar to the Date example above:

1.1.4 Operators

Operators in Scala are actually method calls under the hood. Scala has a set of convenient syntactic shorthands to allow us to write normal-looking code without excessive numbers of parentheses. The most common of these is infix syntax, which allows us to write any expression of the form a.b(c) as a b c, without the full stop or parentheses:

1 .+(2).+(3).+(4) // the space prevents `1` being treated as a double
// res0: Int = 10

1 + 2 + 3 + 4
// res1: Int = 10

1.1.5 Conditionals

Many other syntactic constructs are expressions in Scala, including some that are statements in Java. Conditionals (“if expressions”) are a great example:

// Conditionals ("if expressions"):
if(123 > 456) "Higher!" else "Lower!"
// res6: String = Lower!

1.1.6 Blocks and Side-Effects

Blocks are another type of expression in Scala. Running a block runs each contained expression in order. The type and return value of the block are determined by the last contained expression:

{
  println("First line")
  println("Second line")
  1 + 2 + 3
}
// res0: Int = 6

In functional programming we make the distiction between “pure expressions” and expressions that have “side effects”:

Because the results of intermediate expressions in a block are thrown away, it doesn’t make sense to use pure expressions there. The Scala console even warns us when we try this:

{
  1 + 2 + 3
  4 + 5 + 6
}
// <console>:9: warning: a pure expression does nothing in statement position;
//              you may be omitting necessary parentheses
//                1 + 2 + 3
//                      ^
// res0: Int = 15

The message here is warning us that the intermediate expression 1 + 2 + 3 is wasted computation. All it does is calculate the value 6. We immediately throw the result away and calculate 4 + 5 + 6 instead. We might as well simply write 4 + 5 + 6 and get rid of the block.

Side-effecting expressions, by contrast, make perfect sense within a block. println expressions are a great example of this—they do something useful even though they don’t return a useful value:

{
  println("Intermediate result: " + (1 + 2 + 3))
  4 + 5 + 6
}
// Intermediate result: 6
// res0: Int = 15

Scala developers tend to prefer pure expressions to side-effects because they are easier to reason about. See the section on Substitution for more information. We won’t use blocks in anger until the next chapter when we start declaring intermediate values and re-use them in later expressions.

1.2 Images

Numbers and text are boring. Let’s switch to something more interesting—images! Grab the Doodle project from https://github.com/underscoreio/doodle. This toy drawing library will provide the basis for most of the exercises in this course. Start a Scala console to try Doodle out:

bash$ git clone https://github.com/underscoreio/doodle.git
# Cloning ...

bash$ cd doodle

bash$ ./sbt.sh console
[info] Loading project definition from /.../doodle/project
[info] Set current project to doodle (in build file:/.../doodle/)
[info] Compiling 1 Scala source to /.../doodle/jvm/target/scala-2.11/classes...
[info] Starting scala interpreter...
[info]
import doodle.core._
import doodle.syntax._
import doodle.jvm.Java2DCanvas._
import doodle.backend.StandardInterpreter._
import doodle.examples._
Welcome to Scala version 2.11.7 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_20-ea).
Type in expressions to have them evaluated.
Type :help for more information.

scala> 

1.2.1 Primitive Images

The Doodle console gives us access to some useful drawing tools as well as the regular Scala standard library. Try creating a simple shape:

Circle(10)
// res0: doodle.core.Circle = Circle(10.0)

How To Run This Example

When you see an example like the one above, enter the line of Scala code at the scala> prompt in the console. You should see the text in the comment as output:

scala> Circle(10)
res0: doodle.core.Circle = Circle(10.0)

We haven’t written the scala> prompts in the examples in this book because they make it difficult to copy and paste text into the console. We’ve written the console output as comments for the same reason.

Notice the type and value of the expression we just entered. The type is doodle.core.Circle and the value is Circle(10.0)—a circle with a 10 pixel radius.

We can draw this circle (and other images) using the draw method. Try drawing the circle now:

res0.draw

A window should appear containing the following:

A circle

A circle

Doodle supports a handful “primitive” images: circles, rectangles, and triangles. Let’s try drawing a rectangle:

Rectangle(50, 100).draw
A rectangle

A rectangle

Finally let’s try a triangle:

Triangle(60, 40).draw
A triangle

A triangle

1.2.2 Layout

We can write expressions to combine images producing more complex images. Try the following code—you should see a circle and a rectangle displayed beside one another:

(Circle(10) beside Rectangle(10, 20)).draw
A circle beside a rectangle

A circle beside a rectangle

Doodle contains several layout operators for combining images. Try them out now to see what they do:

Operator Type Description Example
Image beside Image Image Places images horizontally next to one another. Circle(10) beside Circle(20)
Image above Image Image Places images vertically next to one another. Circle(10) above Circle(20)
Image below Image Image Places images vertically next to one another. Circle(10) below Circle(20)
Image on Image Image Places images centered on top of one another. Circle(10) on Circle(20)
Image under Image Image Places images centered on top of one another. Circle(10) under Circle(20)

Exercise: Compilation Target

Create a line drawing of an archery target with three concentric scoring bands:

Simple archery target

Simple archery target

For bonus credit add a stand so we can place the target on a range:

Archery target with a stand

Archery target with a stand

The simplest solution is to create three concentric circles using the on operator:

(Circle(10) on Circle(20) on Circle(30)).draw

For the extra credit we can create a stand using two rectangles:

(
  Circle(10) on
  Circle(20) on
  Circle(30) above
  Rectangle(6, 20) above
  Rectangle(20, 6)
).draw

1.2.3 Colour

In addition to layout, Doodle has some simple operators to add a splash of colour to our images. Try these out to see how they work:

Operator Type Description Example
Image fillColor Color Image Fills the image with the specified colour. Circle(10) fillColor Color.red
Image lineColor Color Image Outlines the image with the specified colour. Circle(10) lineColor Color.blue
Image lineWidth Int Image Outlines the image with the specified stroke width. Circle(10) lineWidth 3

Doodle has various ways of creating colours. The simplest are the predefined colours in shared/src/main/scala/doodle/core/CommonColors.scala. Here are a few of the most important:

Color Type Example
Color.red Color Circle(10) fillColor Color.red
Color.blue Color Circle(10) fillColor Color.blue
Color.green Color Circle(10) fillColor Color.green
Color.black Color Circle(10) fillColor Color.black
Color.white Color Circle(10) fillColor Color.white
Color.gray Color Circle(10) fillColor Color.gray
Color.brown Color Circle(10) fillColor Color.brown

Exercise: Stay on Target

Colour your target red and white, the stand in brown (if applicable), and some ground in green:

Colour archery target

Colour archery target

The trick here is using parentheses to control the order of composition. The fillColor(), lineColor(), and lineWidth() methods apply to a single image—we need to make sure that image comprises the correct set of shapes:

(
  ( Circle(10) fillColor Color.red ) on
  ( Circle(20) fillColor Color.white ) on
  ( Circle(30) fillColor Color.red lineWidth 2 ) above
  ( Rectangle(6, 20) above Rectangle(20, 6) fillColor Color.brown ) above
  ( Rectangle(80, 25) lineWidth 0 fillColor Color.green )
).draw

1.3 Take Home Points

1.3.1 Substitution

In the absence of side-effects, an expression will always evaluate to the same value. For example, 3 + 4 will always evaluate to 7 no matter how many times we compile or run the code.

Given these restrictions, the expressions 3 + 4 and 7 become interchangeable from a user’s point of view. This is known as the subs􏰂titution model of evaluation, although you may remember it as “simplifying formulae” from your maths class at school.

As programmers we must develop a mental model of how our code operates. In the absence of side-effects, the subs􏰂tution model always works. If we know the types and values of each component of an expression, we know the type and value of the expression as a whole.

Functional programmers aim to avoid side-effects for this reason: it makes our programs easy to reason about without having to look beyond the current block of code.

1.3.2 Types in Scala

We’ve seen several types so far, including primitive Scala types such as Int, Boolean, and String, the Date type from the Java standard library, List from the Scala standard library, and the Circle, Rectangle, Image, and Color types from Doodle. Let’s take a moment to see how all of these fit together:

Scala’s type hierarchy

Scala’s type hierarchy

All types in Scala fall into a single inheritance hierarchy, with a grand supertype called Any at the top. Any has two subtypes, AnyVal and AnyRef.

The Unit type is Scala’s equivalent of void in Java or C—we use it to write code that evaluates to “no interesting value”:

val uninteresting = println("Hello world!")
// Hello world!
// uninteresting: Unit = ()

While void is simply a syntax, Unit is an actual type with a single value, (). Having an concrete type for Unit and value allows us to reason about side-effecting code with the same principles as functional code. This is essential for a language like Scala that bridges the worlds of the imperative and the functional.

We have so far seen two imperative-style methods that return Unit: the println method from the Scala standard library and Doodle’s draw method. Each of these methods does something useful but neither returns a useful result:

val alsoUninteresting = Circle(10).draw
// alsoUninteresting: Unit = ()

Designing programs in a functional way involves limiting the side-effects spread throughout our code. Doodle is a classic example of functional design—we assemble a representation of the scene we want in a purely functional manner, and then interpret the scene to produce output. The draw method—our interpreter—can use imperative libraries and mutable state without them intruding into the rest of our application.

2 Declarations

Not all programs are single expressions. Sometimes it is useful to bind expressions to a name and re-use them later. These constructs are called declarations. Declarations themselves don’t have a type or a value. However, they do bind names that can be used as expressions. There are several types of declaration in Scala as we shall see below.

2.1 Value Declarations

The simplest type of declaration binds a name to the result an expression. These are called value declarations. They are similar to variable declarations, except we cannot assign new values to them after declaration:

val blackSquare = Rectangle(30, 30) fillColor Color.black
// blackSquare: doodle.Image = // ...

val redSquare = Rectangle(30, 30) fillColor Color.red
// redSquare: doodle.Image = // ...

Exercise: Chess Board

Create an 8x8 square chess board without loops or comprehensions, using the definitions of redSquare and blackSquare above. Use intermediate value declarations to make your code as compact as possible:

Chess board

Chess board

An 8x8 chess board can be decomposed into four 4x4 boards, each consisting four 2x2 boards, each consisting four squares:

val blackSquare = Rectangle(30, 30) fillColor Color.black
val redSquare   = Rectangle(30, 30) fillColor Color.red

val twoByTwo =
  (redSquare   beside blackSquare) above
  (blackSquare beside redSquare)

val fourByFour =
  (twoByTwo beside twoByTwo) above
  (twoByTwo beside twoByTwo)

val chessBoard =
  (fourByFour beside fourByFour) above
  (fourByFour beside fourByFour)

This is significantly clearer and more compact than creating the whole board in one expression:

val b = Rectangle(30, 30) fillColor Color.black
val r = Rectangle(30, 30) fillColor Color.red

val chessBoard =
  (r beside b beside r beside b beside r beside b beside r beside b) above
  (r beside b beside r beside b beside r beside b beside r beside b) above
  (r beside b beside r beside b beside r beside b beside r beside b) above
  (r beside b beside r beside b beside r beside b beside r beside b) above
  (r beside b beside r beside b beside r beside b beside r beside b) above
  (r beside b beside r beside b beside r beside b beside r beside b) above
  (r beside b beside r beside b beside r beside b beside r beside b) above
  (r beside b beside r beside b beside r beside b beside r beside b)

2.2 Method Declarations

Sometimes we want to repeat a process, but each time we do it we change some part of what we’re doing. For example, imagine creating chess boards where each chess board has a different combination of colors. It would be extremely tedious to declare each chess board as we have above for each choice of colors. What we’d like is to be able to define some process for making chess boards that allows the user to specify the color choice for the particular chess board we’re making. This is what methods allow us to do.

We have already seen method calls. In this section we are going to see how we can declare our own methods. Like value declarations, method declarations define a name. Instead of giving a name to a value, a method declaration gives a name to a process for creating values:

def twoByTwo(color1: Color, color2: Color): Image = {
  val square1 = Rectangle(30, 30) fillColor color1
  val square2 = Rectangle(30, 30) fillColor color2

  (square1 beside square2) above
  (square2 beside square1)
}

This declares a method called twoByTwo. The method has two parameters, called color1 and color2, which we have declared to have type Color. We have also declared the type of the value returned—Image. The body of the method, which is enclosed with optional braces (the { and } pair) defines how we create the Image. This mirrors the process for creating a two by two chess board that we saw above, but in this case we are using the colors we are passed as parameters.

Exercise: Chess Board

Declare a method called fourByFour that constructs a four-by-four chess board using twoByTwo declared above. The method should have two parameters, both Colors, that it passes on to twoByTwo.

You should be able to call fourByFour like so

fourByFour(Color.cornflowerBlue, Color.seaGreen) beside
fourByFour(Color.chocolate, Color.darkSalmon)

to create this picture:

Two Chessboards

Two Chessboards

The structure of fourByFour is identical to twoByTwo except that we use twoByTwo to construct the squares we build the board.

def fourByFour(color1: Color, color2: Color): Image = {
  val square = twoByTwo(color1, color2)

  (square beside square) above
  (square beside square)
}

Syntax Summary

We’ve seen quite a lot of Scala syntax so far. If you can’t remember everything we’ve covered, don’t panic! There’s a syntax quick reference in the appendices at the end of the book.

2.3 Extended Exercise: Color Palettes

In this exercise we will explore the creation of color palettes. An attractive picture must make good choices for color. Color theory has developed to explain combinations of color that go together. We will use color theory to create programs that can automatically construct attractive color palettes.

2.3.1 Color Representation

In Doodle we can represent a color in one of two ways:

  1. as triples containing red, green, and blue values (RGB); or
  2. as hue, saturation, and lightness (HSL).

We will use the HSL representation as it better corresponds to our perception of color. If we arrange colors in the familiar color wheel, distance from the center corresponds to lightness and steps around the outside correspond to changes in hue: