Modern Web Development With Scala Sample
Modern Web Development With Scala Sample
This is a Leanpub book. Leanpub empowers authors and publishers with the Lean Publishing
process. Lean Publishing is the act of publishing an in-progress ebook using lightweight tools and
many iterations to get reader feedback, pivot until you have the right book and build traction once
you do.
2016 Denis Kalinin
Contents
Preface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Before we begin . . . . . . . . . . .
Setting up your environment . .
Installing Java Development Kit
Installing Scala . . . . . . . . . .
Using REPL . . . . . . . . . . .
Using IntelliJ IDEA . . . . . . .
Using Atom Editor . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
2
2
2
3
4
5
6
Language fundamentals . . . . . .
Defining values . . . . . . . . .
Functional types . . . . . . . . .
Type hierarchy . . . . . . . . . .
Collections . . . . . . . . . . . .
Packages and imports . . . . . .
Defining classes . . . . . . . . .
Defining objects . . . . . . . . .
Type parametrization . . . . . .
Collections syntax revisited . . .
Functions and implicits . . . . .
Implicits and companion objects
Loops and conditionals . . . . .
String interpolation . . . . . . .
Traits . . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
8
8
10
11
13
15
16
18
19
20
20
21
22
24
25
Functional programming .
Algebraic data types . .
Pattern matching . . . .
Case classes . . . . . .
Higher-order functions
for comprehensions . .
Currying . . . . . . . .
Laziness . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
26
26
27
27
28
31
31
32
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
CONTENTS
Option . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Try . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Future . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
33
36
39
Preface
As a language, Scala emerged in 2004, but at that time it was known only to a limited circle of
computer scientists and enthusiasts. A couple of years passed and the books started to appear. These
were the books that concentrated mostly on Scala syntax and possibly the standard library. Then, in
mid-2008, Twitter rewrote some of its core functionality in Scala. Foursquare, LinkedIn and Tumblr
soon followed. All of a sudden, people realized that Scala is not a toy for enthusiasts, but a technology
that can benefit todays businesses by allowing them to build high-quality and performant software.
Fast-forward to 2016. Lots of libraries, tools, frameworks written in Scala have appeared. People
are not surprised anymore when they see another Scala success story on InfoQ. Recruiters and
companies are actively looking for Scala developers and in Silicon Valley many big data start-ups
dont even question the choose of the language defaulting to Scala as the obvious choice.
And yet, the majority of public appears to be living in 2005. It sounds crazy, but people still perceive
Scala as one of many niche languages and express doubts about its future. As someone who spent
last two years actively using Play and Akka for building Web apps, I can say that this future is
already here. In fact, its been here at least since the Scala 2.9 release in 2011. While Java stays a
high-performant but relatively low-level language, Scala allows you to use very high-level syntax
constructs without compromising on performance.
I decided to write this book to show how Scala is used. It is not about syntax or functional
programming but about applying Scala for building real-world Web applications. First, we will learn
just enough basics to get started. Then, we will learn some functional programming concepts that
are used in the rest of the book. Finally, we will look at build tools and start using Play framework
for developing a simple Web app. Im not going to overwhelm you with sophisticated one-page long
code samples but rather show a little bit of everything. After reading this book, you will be familiar
with most aspects of Web development in Scala from database access to user authentication. You
will also get a pretty good understanding how to integrate Play with modern frontend tools such as
Webpack and React.
This book is intended for people who only want to start writing Web apps in Scala, so I dont expect
any prior Scala experience. However, I do expect that the reader is familiar with basic Web concepts
such as HTML, CSS, JavaScript and JSON. I also assume that the reader knows well at least one
modern programming language like Java, C#, Ruby or Python.
The book should be read from start to finish, because later more advanced topics are always based
on simpler ones explained earlier. The reader is welcome to try any examples and follow the app
development, but it should also be possible to follow the narrative without a computer.
Lets get started!
Before we begin
If you want to simply read a book in one go without trying to roll out an app of your own, you dont
need any preparations. However, even if this is the case, I would still recommend flicking through
this section, because it provides a good overview of what tools you can use to write Scala code and
how to set them up.
Before we begin
Go to http://www.oracle.com/technetwork/java/javase/downloads/index.html and download the latest JDK 8 (the full name will look something like 8u51, which means version 8,
update 51). If you are on Linux, I recommend downloading a tar.gz file;
On Windows, just follow the instructions of the installer. On Linux, extract the contents of
the archive anywhere (for example, to /DevTools/java8);
Add the bin directory of JDK to your PATH, so that the following command works from any
directory on your machine:
$ java -version
java version "1.8.0_51"
Java(TM) SE Runtime Environment (build 1.8.0_51-b16)
Java HotSpot(TM) 64-Bit Server VM (build 25.51-b03, mixed mode)
Create an environment variable JAVA_HOME and point it to the JDK directory. On Linux, you
can accomplish both goals if you add to your /.bashrc the following:
JAVA_HOME=/home/user/DevTools/jdk1.8.0_51
PATH=$JAVA_HOME/bin:$PATH
Strictly speaking, Scala 2.11 will work perfectly fine with Java 7. However, Play starting
with version 2.4 supports only Java 8, so we will stick to this version.
Installing Scala
You dont need the Scala distribution in order to write Play applications, but you may want to install
it to try some ideas in the interpreter (or REPL read-eval-print loop). Just follow these steps:
Get Scala binaries from http://www.scala-lang.org/download/
Extract the contents of the archive anywhere (for example, to /DevTools/scala)
Add the bin directory of the Scala distribution to your path:
http://www.oracle.com/technetwork/java/javase/downloads/index.html
http://www.scala-lang.org/download/
Before we begin
JAVA_HOME=/home/user/DevTools/jdk1.8.0_51
SCALA_HOME=/home/user/DevTools/scala-2.11.0
PATH=$JAVA_HOME/bin:$SCALA_HOME/bin:$PATH
Using REPL
When you type scala without arguments in the command line, Scala will start the interpreter in
the interactive mode and greet you with a REPL prompt. REPL stands for Read-Eval-Print loop and
it is a great way to learn new features or try out some ideas. You can start by typing
scala> println("Hello Scala")
and then you can press Enter and observe the output.
Hello Scala
Since the println function prints a message in stdout and the REPL session is hooked to stdout,
you see the message in the console. Another option is to simply type
scala> "Hello World"
in the console. The REPL evaluates an expression, determines its type and, if you havent assigned
it to anything, assigns it to an automatically-generated value.
By the way, The Scala Worksheet plugin in Atom works by intercepting stdout of the REPL
and presenting its output as a comment in the editor area.
Feel free to come back to REPL later, when you learn more about the language, but for now here is
the list of commands that you may find particularly useful:
Before we begin
:quit
:reset
:cp
:paste
:history
:help
You can safely ignore them for now. By the way, you can use the idea.vmoptions file that resides
in the bin directory to tweak, well, VM options. Two properties which are often tweaked are:
-Xms128m
-Xmx2048m
Here -Xms specifies the size of the initial memory pool and -Xmx specifies the maximum. And if you
want to learn more about them, start with this question on StackOverflow.
Sometimes IntelliJ goes crazy and starts highlighting valid parts of your code as errors. When
it happens, there are two ways to fix the IDE. The simplest is to deactivate Scala type-aware
highlighting and then activate it again. This helps in many cases.
A more radical solution is to click File -> Invalidate Caches / Restart. IDEA will have to reindex
everything after restart, but sometimes it could be the only way to regain its senses.
https://www.jetbrains.com/idea/
http://stackoverflow.com/questions/14763079/
Before we begin
There are many packages written for Atom and for trying examples from this book you may want
to install the following:
language-scala - adds syntax highlighting for Scala
linter-scalac - compiles your code behind the scenes using scalac to show compiler errors
right inside the editor
terminal-plus - allows to use the terminal session without leaving the editor
scala-worksheet-plus - enables the worksheet mode, which executes your code in the Scala
interpreter behind the scenes and shows results as comments
Just go to Packages -> Settings View -> Install Packages/Themes.
Before we begin
Everything related to frontend is usually top quality in Atom. At the same time, full support
of JavaScript and CSS is considered a paid feature in IntelliJ. The Community version will
paint HTML markup in different colors and even highlight JS files, but if you want support
for JSX templates or CSS preprocessors, you should start looking at the Ultimate version or
switch to Atom.
Lastly, I found the Scala Worksheet plugin in Atom to be more stable than its counterparts
in IntelliJ or Eclipse. Besides, even if you encounter a bug in a plugin, all source code is in
/.atom, so its usually fixable.
I suggest you use Atom for the first parts of the book and then switch to IntelliJ for working with
Play. Please refer to Appendix B for additional instructions on importing Scala projects in IntelliJ.
Language fundamentals
In this section, I will demonstrate the basics of the Scala programming language. This includes things
that are common to most programming languages as well as several features which are specific to
Scala. Im going to provide the output of the interpreter here, but feel free to start you own REPL
session and try it yourself.
Defining values
There are three main keywords for defining everything in Scala:
val
var
def
By the way, there are several ways to define a function in Scala and using def is only one
of them. Read on!
Notice that Scala guessed the type of the constant by analyzing its value. This feature is called type
inference.
Sometimes you want to specify the type explicitly. If in the example above you want to end up with
a value of type Short, simply type
scala> val num: Short = 42
num: Short = 42
Variables are rarely needed in Scala, but they are supported by the language and could be useful
sometimes:
Language fundamentals
Just as with types you may explicitly specify the type or allow the type inferrer to do its work.
As we will see later, all statements in Scala evaluate to some value. This feature essentially
allows you to write code using vals and resort to vars only occasionally.
When defining methods it is required to specify argument types. Specifying the return type is
recommended but optional:
scala> def greet(name: String) = "Hello" + name
greet: (name: String)String
Its worth mentioning that the body doesnt require curly braces and could be placed on the same
line as the signature. At the same time, relying on the type inferrer for guessing the return type is
not recommended for a number of reasons:
If you rely on the type inferrer, it basically means that the return type of your function depends
on its implementation, which is dangerous as implementation is often changed;
The readers of your code dont want to spend time guessing the resulting type.
This is particularly true when you work on public APIs.
The return type can be specified after the parentheses:
scala> def greet(name: String): String = "Hello " + name
greet: (name: String)String
The value that is returned from the method is the last expression of its body, so in Scala you dont
need to use the return keyword. Remember, however, that the assignment operator doesnt return
a value.
If you want to create a procedure-like method that doesnt return anything and is only invoked for
side effects, you should specify the return type as Unit. This is a Scala replacement for void (which,
by the way, is not even a keyword in Scala):
scala> def greet2(): Unit = println("Hello World")
greet2: ()Unit
Language fundamentals
10
Not so long ago, the official documentation recommended omitting the equals sign
when defining procedure-like methods. Not anymore! Starting with version 2.10, the
recommendation is to use the equals sign, always. Check this StackOverflow question
or the docs.
Its also possible to define a method without parentheses, in which case it is called a parameterless
method:
scala> def id = Math.random
id: Double
scala> id
res10: Double = 0.15449866168176285
This makes the invocation of such a method look like accessing a variable/constant and supports
the uniform access principle. If you want to define your method like this, you should ensure that
it doesnt have side effects, otherwise it will be extremely confusing for users.
Functional types
Its possible to define a function and assign it to a variable (or constant). For example, the greet
method from the example above could also be defined the following way:
scala> var greetVar: String => String = (name) => "Hello " + name
greetVar: String => String = <function1>
As REPL output shows, String => String is the type of our function. It can be read as a function
from String to String. Again, you dont have to type the return type, but if you want the type inferrer
to do the work, youll have to specify the type of parameters:
scala> var greetVar = (name: String) => "Hello " + name
greetVar: String => String = <function1>
Assigning methods to variables is also possible, but requires using special syntax to tell the compiler
that you want to reference the function rather than call it:
http://stackoverflow.com/questions/944111/
http://docs.scala-lang.org/style/declarations.html#procedure_syntax
http://stackoverflow.com/questions/7600910/
11
Language fundamentals
Type hierarchy
Since we are on the topic of types, lets take a look at a very simplified type hierarchy:
Unlike Java, literally everything has its place here. Primitive types inherit from AnyVal, reference
types (including user-defined classes) inherit from AnyRef. The absence of value has its own type
Unit, which belongs to the primitives group. Even functions have their place in this hierarchy: for
example, a function from String to String has type Function1[String, String]:
scala> val greetFn = (name: String) => "Hello " + name
greetFn: String => String = <function1>
scala> val fn: Function1[String, String] = greetVar
fn: String => String = <function1>
12
Language fundamentals
By
the
way,
another
name of scala.AnyRef
str.isInstanceOf[Object] returns true
is
java.lang.Object,
so
Scala also introduces the concept of so-called bottom types which implicitly inherit from all other
types:
In the diagram above, Null implicitly inherits from all reference types (including user-defined ones,
of course) and Nothing from all types including primitives and Null itself. You are unlikely to use
bottom types directly in your programs, but they are useful to understand type inference. More on
this later.
Language fundamentals
13
Collections
Unlike Java, Scala uses the same uniform syntax for creating and accessing collections. For example,
you can use the following code to instantiate a new list and then get its first element:
scala> val list = List(1,2,3,4)
list: List[Int] = List(1, 2, 3, 4)
scala> list(0)
res24: Int = 1
For comparison, here is the same code that uses an array instead of a list:
scala> val array = Array(1,2,3,4)
array: Array[Int] = Array(1, 2, 3, 4)
scala> array(0)
res25: Int = 1
For performance reasons, Scala will map the latter collection to a Java array. It happens behind the
scenes and is completely transparent for a developer.
The collections API was completely rewritten in Scala 2.8, which in turn was released in
2009. Even though many regard this rewrite as a significant improvement, some people
were quite skeptical back then.
Another important point is that Scala collections always have a type. If the type wasnt specified,
it will be inferred by analyzing provided elements. In the example above we ended up with the list
and array of Ints because the elements of these collections looked like integers. If we wanted to
have, say, a list of Shorts, we would have to set the type explicitly like so:
scala> val list = List[Short](1,2,3,4)
list: List[Short] = List(1, 2, 3, 4)
If the type inferrer cannot determine the type of the collection, compilation will fail:
http://stackoverflow.com/questions/1722726/
14
Language fundamentals
Collections Hierarchy
Its worth mentioning that most commonly used methods are already defined on very basic
collection types. In practice, this means that when you need a collection, usually you can simply
use Seq and its API will probably be sufficient most of the time.
Collections also come in two flavours - mutable and immutable. Scala defaults to the immutable
flavour, so when in the previous example we typed List, we actually ended up with the immutable
List:
1
2
scala> list.isInstanceOf[scala.collection.immutable.List[Short]]
res29: Boolean = true
Immutable collections dont have methods for altering the original collection, but they have methods
that return new collections. For example, the :+ method returns a new list that contains all the
elements of the original list and one new element:
Language fundamentals
15
Scala imposes very few restrictions on function names, so it is perfectly acceptable to create
a function called :+. In addition, if the function has only one parameter, it is possible to
omit the dot and parentheses and make a method invocation look like using an operator.
This approach is widely used in the standard library, so when you use the mathematical
plus operator to add two integers, you actually use a method called + defined on Int.
Therefore, in Scala 2 + 2 can also be written as 2.+(2) and vice versa.
Mutable collections can alter themselves. A great example of a mutable list is ListBuffer, which is
often used to accumulate values and then build an immutable list from these values:
scala> val buffer = scala.collection.mutable.ListBuffer.empty[Int]
buffer: scala.collection.mutable.ListBuffer[Int] = ListBuffer()
scala> buffer += 1
res35: scala.collection.mutable.ListBuffer[Int] = ListBuffer(1)
scala> buffer += 2
res36: scala.collection.mutable.ListBuffer[Int] = ListBuffer(1, 2)
scala> buffer.toList
res37: List[Int] = List(1, 2)
Language fundamentals
16
Just like in Java or C#, we can import all classes from a particular package using a wildcard. The
difference is that Scala uses _ instead of *:
scala> import scala.collection.mutable._
import scala.collection.mutable._
scala> val set = HashSet[Int]()
set: scala.collection.mutable.HashSet[Int] = Set()
In addition to the imports defined by a developer, Scala automatically imports the following:
java.lang._, which includes String, Exception etc.
scala._, which includes Option, Any, AnyRef, Array etc.
scala.Predef, which conveniently introduces aliases for commonly used types and functions
such as List, Seq, println etc.
Imports in Scala are not restricted to the beginning of the file and can appear inside functions or
classes.
Defining classes
Users can define their own types using the omnipresent class keyword:
scala> class Person {}
defined class Person
Needless to say, the Person class defined above is completely useless. You can create an instance of
this class but thats about it:
scala> val person = new Person
person: Person = Person@794eeaf8
Note that the REPL showed Person@794eeaf8 as a value of this instance. Why is that? Well, in
Scala all user-defined classes implicitly extend AnyRef, which is the same as java.lang.Object.
The java.lang.Object class defines a number of methods including toString:
Language fundamentals
17
By convention, the toString method is used to create a String representation of the object, so it
makes sense that REPL uses this method to print a value of a user-defined class in the console.
A slightly better version of this class can be defined as follows:
scala> class Person(name: String) {}
defined class Person
By typing name: String inside the parentheses we defined a constructor with a parameter of type
String. Although it is possible to use name inside the class body, the class still doesnt have a field
to store it. Fortunately, its easily fixable:
scala> class Person(val name: String) {}
defined class Person
Much better! Adding val in front of the parameter name defines an immutable field. Similarly
var defines a mutable field. In any case, this field will be initialized with the value passed to the
constructor when the object is instantiated. Whats interesting, the class defined above is roughly
equivalent of the following Java code:
1
2
3
4
5
6
7
8
9
class Person {
private final String mName;
public Person(String name) {
mName = name;
}
public String name() {
return mName;
}
}
By the way, if you dont have anything inside the body of your class, you dont need curly
braces.
Language fundamentals
1
2
3
4
5
18
When working with classes and objects in REPL, it is often necessary to type several lines
of code at once. Remember that REPL provides the paste mode, which can be activated by
the :paste command.
In the example above we defined two methods - apply and sayHello and overrode (thus keyword
override) existing method toString. Of these three, the simplest one is sayHello as it only prints
a phrase to stdout:
scala> val joe = new Person("Joe")
joe: Person = Person(Joe)
scala> joe.sayHello()
res49: String = Hi! I'm Joe
Notice that REPL used our toString implementation to print information about the instance to
stdout. As for the apply method, there are two ways to invoke it.
scala> joe.apply()
res50: String = Joe
scala> joe()
res51: String = Joe
Yep, using parentheses on the instance of a class actually calls the apply method defined on this
class. This approach is widely used in the standard library as well as in third-party libraries.
Defining objects
Scala doesnt have the static keyword but it does have syntax for defining singletons. If you need
to define methods or values that can be accessed on a type rather than an instance, use the object
keyword:
https://en.wikipedia.org/wiki/Singleton_pattern
Language fundamentals
1
2
3
19
object RandomUtils {
def random100 = Math.round(Math.random * 100)
}
After RandomUtils is defined this way, you will be able to use method random100 without creating
any instances of the class:
scala> RandomUtils.random100
res62: Long = 67
You can define the apply method on an object and then call it using the name of the object followed
by parentheses. One common pattern is to define an object that has the same name as the original
class and define the apply method with the same constructor parameters as the original class:
1
2
3
4
5
6
If an object has the same name as a class, then its called a companion object. Companion
objects are often used in Scala for defining additional methods and implicit values. You
will see a concrete example when we get to serializing objects into JSON.
Now you can use the apply method on the object to create instances of class Person:
scala> val person = Person("Joe")
person: Person = Person(Joe)
Essentially, this eliminates the need for using the new keyword. Again, this approach is widely used
in the standard library and in third-party libraries. In later chapters I usually refer to this syntax as
calling the constructor even though it is actually calling the apply method on a companion object.
Type parametrization
Classes can be parametrized, which makes them more generic and possibly more useful:
Language fundamentals
1
2
3
20
We defined a class called Cell and specified one field contents. The type of this field is not yet
known. Scala doesnt allow raw types, so you cannot simply create a new Cell, you must create a
Cell of something. Fortunately, the type inferrer can help with that:
scala> new Cell(1)
res71: Cell[Int] = Cell@1c0d3eb6
Here we passed 1 as an argument and it was enough for the type inferrer to decide on the type of
this instance. We could also specify the type ourselves:
scala> new Cell[Short](1)
res73: Cell[Short] = Cell@751fa7a3
First, we can conclude that List is a parametrized class that has the apply method. This method
accepts an index as an argument and returns the element that has this index. Second, there is also
an object called List which has the apply method. This method probably calls the List constructor
to instantiate the actual object. Finally, the toString method is overridden to return the word List
and its elements in parentheses.
This is actually a recurring idea in Scala design. Rather than introduce specifics, Scala
always tries to solve a particular problem using a more universal or generic approach.
Language fundamentals
21
Compiler sees that the argument is absent, but it also knows that there is a default value, so it takes
this value and then invokes the function as usual.
We can go even further and declare the name parameter as implicit:
scala> def greet(implicit name: String) = "Hello " + name
greet: (implicit name: String)String
Here were not setting any default values in the function signature. As a result, in the absence of the
argument, Scala will look for a value of type String marked as implicit and defined somewhere
in scope. So, if we try to call this function immediately, it will not work:
scala> greet
<console>:9: error: could not find implicit value for parameter name: String
greet
^
However, after defining an implicit value with the type String, it will:
scala> implicit val n = "User"
n: String = User
scala> greet
res79: String = Hello User
This code works because there is exactly one value of type String marked as implicit and defined
in the scope. If there were several implicit Strings, the compilation would fail due to ambiguity.
Language fundamentals
1
2
3
4
5
22
Lets also create a method with one parameter of type Person and mark it as implicit:
def sayHello(implicit person: Person): String = "Hello " + person.name
Even if we dont create any instances of class Person in the scope, we will still be able to use the
sayHello method without providing any arguments:
scala> sayHello
res0: String = Hello User
This works because companion objects are another place where the compiler looks for implicits. The
point here is that the type of the method parameter is the same as the name of the companion object.
Of course, we can always pass a regular argument explicitly:
scala> sayHello(new Person("Joe"))
res1: String = Hello Joe
Some people argue that implicits make code unmaintainable. In my experience, however,
it has never been the case. In fact, most professional Scala developers agree that implicits
actually make code clearer.
23
Language fundamentals
1
2
3
4
5
6
When this method is defined in REPL, the interpreter responds with isEven: (num: Int)Boolean.
Even though we havent specified the return type, the type inferrer determined it as the type of the
last (and only) expression, which is the if expression. How did it do that? Well, by analyzing the
types of both branches:
if-branch
else-branch
whole expression
Boolean
Boolean
Boolean
If an argument is an even number, the if branch is chosen and the result type has type Boolean. If
an argument is an odd number, the else branch is chosen but result still has type Boolean. So, the
whole expression has type Boolean regardless of the winning branch, no rocket science here.
But what if branch types are different, for example Int and Double? In this case the nearest common
supertype will be chosen. For Int and Double this supertype will be AnyVal, so AnyVal will be the
type of whole expression.
If you give it some thought, it makes sense, because the result type must be able to hold values of
both branches. After all, we dont know which one will be chosen until runtime.
AnyRef
Null
AnyRef
OK, what if the else-branch never returns and instead throws an exception? In this case, the type
of the else-branch is considered to be Nothing and the whole expression will have the type of the
if-branch.
24
Language fundamentals
if-branch
else-branch
whole expression
Any
Nothing
Any
Theres not much you can do with bottom types, but they are included in Scala hierarchy and they
make the rules that the type inferrer uses more clear.
while loops
The while loop is almost a carbon copy of its Java counterpart, so in Scala it looks like an outcast.
Instead of returning a value, its called for a side effect. Moreover, it almost always utilizes a var for
iterations:
1
2
3
4
5
var it = 5
while (it > 0) {
println(it)
it -= 1
}
It feels almost awkward to use it in Scala and as a matter of fact you almost never need to. We will
look at some alternatives when we get to the Functional programming section but for now here is
more Scala-like code that does essentially the same thing:
1.to(5).reverse.foreach { num => println(num) }
String interpolation
String interpolation was one of the features introduced in Scala 2.10. It allows to execute Scala code
inside string literals. In order to use it, simply put s in front of a string literal and $ in front of any
variable or value you want to interpolate:
1
2
If an interpolated expression is more complex, e.g contains dots or operators, it needs to be taken in
curly braces:
1
2
Language fundamentals
25
Traits
Traits in Scala are similar to mixins in Ruby in a sense that you can use them to add functionality
to your classes. Traits can contain both abstract (not implemented) and concrete (implemented)
members. If you mix in traits with abstract members then you must either implement them or mark
your class as abstract, so the Java rule about implementing interfaces still holds.
In Java, a class can implement many interfaces, but if you want to make your class concrete
(i.e. allow to instantiate it), you need to provide implementations for all methods defined
by all interfaces. Unlike Java interfaces, though, Scala traits can and often do have concrete
methods.
We defined two traits so that trait A has one concrete method and trait B has one abstract method
(the absence of a body that usually comes after the equals sign means exactly that). If we want to
create a new class C that inherits functionality from both traits, we will have to implement the b
method:
class C extends A with B { def b(): Unit = println("b") }
If we dont implement b, we will have to make the C class abstract or get an error.
Functional programming
In this section we will examine a number of features which support functional programming and are
available in Scala. As we will see, all of them are actively used by library designers and developers
in real-world applications.
List as an ADT
The important thing here is that the list may be either empty or non-empty, i.e Nil and :: form
disjoint sets. In addition to using the List objects apply method, you can use the following approach
to construct a new list:
val list = 1 :: 2 :: 3 :: Nil
The :: method is called prepend and its available on all lists including the empty one. In Scala, if a
methods name ends with a colon (:), the argument of this method (when the method invocation is
written in the operator style) must be placed left, so the above code transforms into following:
val list = Nil.::(3).::(2).::(1)
Basically, we start with the empty list Nil and then prepend the numbers, so that the resulting list
will be List(1, 2, 3).
In order to determine the type of the list we could use the isInstanceOf method available on all
types:
26
27
Functional programming
1
2
3
list.isInstanceOf[List[Any]]
list.isInstanceOf[Nil.type]
list.isInstanceOf[::[Int]]
// true
// false
// true
Note that since Scala doesnt allow raw types, we had to specify Any and Int even though
we werent interested in such details. On the other hand, Nil is defined as object (singleton)
and represents the empty list regardless of the element type.
Pattern matching
You can think of pattern matching as Javas switch statement on steroids. The main differences are
that pattern matching in Scala always returns a value and it is much more powerful.
1
2
3
4
list match {
case Nil => println("empty")
case ::(head, tail) => println("non-empty")
}
The first case block simply checks whether the list object is the Nil singleton object. The second
one is more interesting, though. Not only does it check that the list has type ::, it also extracts
head (the first element) and tail (all elements except the first one). If you want your class to have
similar capabilities, you must either provide the unapply method on the companion object or turn
your class into a case class.
Case classes
Remember our Person class from the section about objects? Lets revisit that code here:
1
2
3
4
5
6
Here were creating a class, defining a field, overriding the toString method, defining a companion
object. All of this could be achieved by the following line:
Functional programming
28
On top of the goodies mentioned above, this definition also does the following:
overrides the hashcode method used by some collections from the standard library
overrides the equals method, which makes two objects with the same set of values equal, so
new Person("Joe") == new Person("Joe") will return true
defines the unapply method used in pattern matching
As a result, even though the word case suggests that case classes are somehow related to
pattern matching, many developers create them to quickly get hassle-free types with hashcode/equals/apply methods.
Higher-order functions
Functions that accept other functions as their arguments are called higher-order functions. They
are supported by virtually any modern programming language including JavaScript, Ruby, Python,
PHP, Java, C# and so on. Since they are such a common concept, which is probably already familiar
to you, were not going to spend much time discussing the theory behind higher-order functions.
Instead we will look at some Scala specifics.
It may not be that useful when the only argument is a String, but what if the function accepts
another function as the argument? If we had to always use parentheses, there would be to many of
them:
https://en.wikipedia.org/wiki/Higher-order_function
29
Functional programming
But since the invokeBlock method accepts only one argument, we can use the following syntax:
1
2
3
invokeBlock { () =>
println("Inside the block!")
}
The block spreads to several lines and using curly braces makes the code actually look like a DSL
(Domain-Specific Language).
Arguably, the most used method in the entire Collection API is map. It accepts a function that will
be used to transform collection elements one by one. For example:
list.map { el => el * el }
// List(1, 4, 9, 16)
Here each element is multiplied by itself. Note that the original list stays untouched because instead
of changing it, map returns a new list.
Another method is filter, which allows to keep only those elements that satisfy the condition:
list.filter { el => el % 2 == 0 }
// List(2, 4)
30
Functional programming
// prints 1234
flatMap
Lets assume that in addition to the list defined above - List(1,2,3,4) - we have another list of
strings:
val list2 = List("a", "b")
What if we wanted to combine each element of the first list with each element of the second list?
Lets try to solve this task by nesting two maps:
1
2
3
4
5
We ended up with a list of lists. Not quite what we expected, right? The problem here is the external
map. If you look at the Scala documentation, you will see that the map method accepts a function that
takes one element and returns another element. On the other hand, we want to pass a function that
takes an element and returns a list. Is there such a function defined on List? It turns out that yes,
and its called flatMap. Using flatMap the problem can be solved easily:
1
2
3
4
5
31
Functional programming
for comprehensions
In order to simplify writing expressions that use map, flatMap, filter and foreach, Scala provides
so-called for comprehensions. Oftentimes for comprehensions make your code clearer and easier to
understand.
Lets rewrite the previous examples with for expressions:
1
2
3
// List(1, 4, 9, 16)
// List(2,4)
// prints 1234
The yield keyword is used when the whole expression returns some value. With foreach we are
simply printing values on the screen, thus theres no yield keyword.
The flatMap example translates into the following:
1
2
3
4
for {
el1 <- list
el2 <- list2
} yield el1 + el2
In my opinion, the last snippet can be easily understood even by people who dont know what
flatMap is and even that this function exists.
For comprehensions are the central part of the Scala programming language. They are used in many
situations and you will see a lot of examples with the for keyword throughout this book.
If you want to use your classes in for comprehensions, you must provide at least two
methods - map and flatMap. The other two are necessary only if you plan on filtering
and iterating with for.
Currying
A curried function is a function that has each of the parameters in its own pair of parentheses. The
process of transforming a regular function into a curried one is called currying. A simple function
that sums two integers
https://en.wikipedia.org/wiki/Currying
Functional programming
32
If the function is curried, it must be called with each argument in its own pair of parentheses:
sum(2)(2)
// 4
foldLeft
The foldLeft method from the Collections API is a curried method that takes two parameters.
First parameter - the initial value - is used as the staring point of the computation and the second
parameter is a function that describes how to compute the next value using the previous one and the
accumulator. It may sound terribly complicated, but in reality foldLeft is absolutely straightforward
to use.
For example, we can use foldLeft to calculate the sum of elements in our list - List(1,2,3,4):
1
2
3
Here we replaced parentheses around the second argument with curly braces to make it look like a
proper function.
Another (more advanced) reason for making foldLeft curried is type inference. By looking
at the first argument, the type inferrer gets more insight into the types used in the second
argument. Check this question on StackOverflow if you want more details.
Laziness
By default, all values in Scala are initialized eagerly at the moment of declaration. This approach is
widely known and in many programming languages there is no other way. In Scala, however, values
can be initialized lazily, which means they remain uninitialized until the first use.
Lets create a fictional service that we will use for demonstration:
http://stackoverflow.com/questions/4915027/
33
Functional programming
1
2
3
class ConnectionService {
def connect = println("Connected")
}
The ConnectionService has only one method. Obviously, the service must be instantiated somewhere before its used. The following code reminds us that the order of initialization matters:
1
2
3
4
The code will fail at runtime with NullPointerException because when we declared the serviceRef, the service itself hadnt been initialized. Ironically, when we first tried to use it, the
service was up and running, so the problem was the order of initialization, not the service being
uninitialized. If only we could postpone the initialization of serviceRef! It turns out that in Scala
we can do exactly that by putting the lazy keyword in front of the val declaration (line 2).
2
In this case, the serviceRef is not initialized until serviceRef first referenced. By this time, the
service is already up and running, so everything works fine.
Lazy values are often used in Scala for configuration and dependency injection (DI). In Java
building a dependency graph was a task performed by various DI frameworks. In Scala this
task can be delegated to the compiler.
Option
Option is a container that may or may not contain a value. It is another example of an algebraic
data type, because it is formed by two subtypes: None and Some.
Option as ADT
34
Functional programming
Options are a great way to communicate that a particular value may be absent. This helps to always
avoid NullPointerExceptions if you play by the rules. Options are used everywhere in the standard
library, in third-party libraries and they should be used in user code as well.
The safest way to create a value of type Option is to specify the type and then pass the value as the
only argument:
scala> Option[String]("Joe")
res108: Option[String] = Some(Joe)
This way you can guarantee that the type will be Option[String] and you never end up with nullvalues inside. Even if you pass null, you will get None:
scala> Option[String](null)
res107: Option[String] = None
In many situations you can also pass a value in Some, but this approach doesnt guarantee that the
type will be what you expect:
scala> Some(1)
res109: Some[Int] = Some(1)
In the example above we expected to get more generic Option[Int] but instead received more
narrow Some[Int]. Moreover, we may end up with nulls inside, which is a very dangerous situation
and brings back the possibility of NullPointerExceptions:
scala> Some(null)
res104: Some[Null] = Some(null)
description
isDefined
isEmpty
getOrElse
get
map
foreach
Functional programming
35
Lets look at one concrete example to demonstrate how options are used. Imagine that we have
a map that may contain information about the user. Here were interested in two particular keys
firstName and lastName and we want to use them to construct a value for full name.
1
2
3
4
There are several ways to initialize a map and this is only one of them. Note, that using ->
for creating key-value pairs is a neat trick that is sometimes used in Scala. If you want to
learn more about this particular use case, check out this question on StackOverflow.
The apply method on Map returns the value associated with the requested key if the key is present or
throws an exception if it is not. So, if we know for sure that this map contains both keys, constructing
the full name is trivial:
val fullName = data("firstName") + " " + data("lastName")
If we dont know what information is inside the map, then we need to use the get method, which
returns an Option.
1
2
Obviously, we cannot concatenate two Options as we would Strings, but we can obtain Strings by
using the get method on Option:
1
2
3
4
When working with Options, we must first check whether the value actually exists and then use the
get method.
A slightly more Scala-like approach would involve pattern matching:
http://stackoverflow.com/questions/4980515/
Functional programming
1
2
3
4
5
36
Here were grouping two values together by enclosing them in parentheses. Then we match the
newly created pair against two case blocks. The first block works if both Options have type Some
and therefore contain some values inside. The second block defines a so-called catch-all case, which
is used if nothing else succeeded so far.
We can also achieve the same result by using map/flatMap combination:
1
2
3
4
5
Doesnt it look familiar? It certainly does! When we were working with lists, we found out that this
construct can be written as a for comprehension:
1
2
3
4
Great, isnt it? This looks almost as straightforward as String concatenation. Please, memorize this
construct, because as we will see later, it is used in Scala for doing a great deal of seemingly unrelated
things like working with dangerous or asynchronous code.
Try
Lets write a fictitious service that works roughly 60% of the time:
Functional programming
1
2
3
4
5
6
7
8
37
object DangerousService {
def queryNextNumber: Long = {
val source = Math.round(Math.random * 100)
if (source <= 60)
source
else throw new Exception("The generated number is too big!")
}
}
If we start working with this service directly without taking any precautions, sooner or later our
program will blow up:
scala> DangerousService.queryNextNumber
res118: Long = 27
scala> DangerousService.queryNextNumber
java.lang.Exception: The generated number is too big!
at DangerousService$.queryNextNumber(<console>:14)
... 32 elided
To work with dangerous code in Scala you can use try-catch-finally blocks. The syntax slightly
differs from Java/C#, but the main idea is the same: you wrap dangerous code in the try block and
then if an exception occurs, you can do something about it in the catch block.
1
2
3
4
5
6
The good news about try blocks in Scala is that they return a value, so you dont need to define a
var before the block and then assign it to some value inside (this approach is extremely common
in Java). The bad news is that even if you come up with some reasonable value to return in case
of emergency, this will essentially swallow the exception without letting anyone know what has
happened. In theory, we could return an Option, but the problem here is that None will not be able
to store information about the exception.
A better alternative is to use Try from the standard library. The Try type is an algebraic data type
that is formed by two subtypes - Success and Failure.
38
Functional programming
Try as ADT
The Try class provides many utility methods that make working with it very convenient:
method
description
isSuccess
isFailure
get
toOption
map
foreach
The Try type is defined in scala.util, so you need to use import scala.util.Try if you
want to use Try without typing its fully qualified name.
When working with Try, the dangerous code can be simply put in the Try constructor, which will
catch all exceptions if any occur:
1
2
If we want to sum two values wrapped in Trys, we can use the already familiar flatMap/map
combination:
1
2
3
4
5
Functional programming
1
2
3
4
39
Note that a for comprehension yields the same container type (in our case Try) that was used initially.
This happens because maps and flatMaps can merely transform the value inside the container, but
they cannot change the type of the container itself. If at some point you need to convert Try into
something else, you will need to use utility methods but not maps or flatMaps.
Future
The final piece of the standard library that we are going to examine here is the Future class. Futures
allow you to work with asynchronous code in a type-safe and straightforward manner without
resorting to concurrent primitives like threads or semaphores.
Many methods defined on the Future class are curried and accept an implicit argument. For
example, take a look at the map signature:
def map[S](f: (T) => S)(implicit executor: ExecutionContext): Future[S]
This definition allows users of this class to declare or import some default ExecutionContext and
then work with Futures as if they were regular Scala containers like Options or Trys. The global
ExecutionContext is already defined in the standard library and the only thing thats left is to import
it:
import scala.concurrent.ExecutionContext.Implicits.global
Lets add a pause to our service to make it slower and therefore simulate working with something
very remote:
1
2
3
4
5
6
7
8
9
object DangerousAndSlowService {
def queryNextNumber: Long = {
Thread.sleep(2000)
val source = Math.round(Math.random * 100)
if (source <= 60)
source
else throw new Exception("The generated number is too big!")
}
}
If we try to use this service synchronously, our program, which uses this service, will be slowed
down. In order to avoid it, we need to wrap our calls to DangerousAndSlowService in a Future:
40
Functional programming
1
2
The important thing here is that both number1F and number2F will be initialized immediately.
However, we will not be able to use the actual numbers until they are returned from the service.
The Future class provides many methods that you can use:
method
description
onComplete
onFailure
onSuccess
mapTo
map
foreach
However, this approach makes code very complicated extremely quickly by introducing so-called
callback hell. Besides, even after your callback is executed its usually not obvious how to
communicate the result (or lack thereof) to the rest of the program.
A better solution is to utilize map and flatMap:
1
2
3
4
5
Ana again, the same code could also be written as a for comprehension:
Functional programming
1
2
3
4
41
Usually, if you have only one container (Option, Try, Future, Seq and so on), its easier to simply
use map. If you have several containers, for comprehensions will be a better choice.
Youve probably noticed that in several examples we used functional blocks that start
with the case keyword. These functional blocks are called partial functions. A function
is called partial if it is defined only for a limited set of arguments. In practice, just
remember that if you want to use a higher-order function that accepts a parameter of
type PartialFunction[T, R], you are expected to provide a functional block that starts
with case.
https://en.wikipedia.org/wiki/Partial_function