Hands-on Scala teaches you how to use the Scala programming language in a practical, project-based fashion. This book is designed to quickly teach an existing programmer everything needed to go from "hello world" to building production applications like interactive websites, parallel web crawlers, and distributed systems in Scala. In the process you will learn how to use the Scala language to solve challenging problems in an elegant and intuitive manner.
Hands-on Scala is available now as an E-Book in PDF, EPub, and Mobi formats, or as a Paperback on Amazon. Currently 50% off until the end of the month!
Hands-On Scala is the best way to learn about writing Scala in this simple and straightforward manner, and a great resource for getting things done using the Scala ecosystem.
I helped review some of this book - excellent work by @li_haoyi; if you're looking to get into Scala, recommended!
Fantastic book! I got the privilege to review it, and I can honestly say it made me a better engineer! Tour de force by @li_haoyi
This will be great! @li_haoyi asked me to read an early draft. Very pragmatic. Great examples.
Hands-on Scala is designed for professional developers who need to get up to speed using Scala in production. This book dives straight into use cases: you will write interactive websites, network file synchronizers, parallel web crawlers, data migration tools, and much more. Every chapter not only teaches language concepts, but also walks you through a use case that the author has worked on professionally and will be a valuable addition to your developer toolbox.
Knowing the language alone isn't enough to go to production: Hands-on Scala introduces the reader to the ecosystem of editors, build tools, web frameworks, database libraries, everything necessary to do real work using Scala. You will finish this book having all the necessary building blocks to be productive using Scala in production.
Hands-on Scala starts and ends with working code. The concepts you learn in this book are backed up by over 120 executable code examples that demonstrate the concepts in action, and every chapter ends with a set of exercises with complete executable solutions. More than just a source of knowledge, Hands-on Scala's wide variety of working code examples also serve as a cookbook you can use to kickstart any project you may work on in future.
@ requests.
post(
"https://api.github.com/repos/lihaoyi/test/issues"
,
data =
ujson.
Obj(
"title"
->
"hello"
)
,
headers =
Map(
"Authorization"
->
s"token $token"
)
)
res56:
requests.
Response =
Response(
"https://api.github.com/repos/lihaoyi/test/issues"
,
201
,
"Created"
,
.
.
.
Python's convenience with Go's performance and scalability, Scala gives you the best of both worlds. Scala's conciseness makes rapid prototyping a joy, while its optimizing compiler and fast JVM runtime provides great performance to support your heaviest production workloads.
@ printHello(
"1"
)
// wrong type of argument
>
cmd128.
sc:
1
:
type
mismatch;
>
found :
String
(
"1"
)
>
required:
Int
>
val
res128 =
printHello(
"1"
)
>
^
>
Compilation Failed
Tired of fighting TypeError
s and NullPointerException
s in
production? Scala's functional programming style and
type-checking compiler helps rule out entire classes of bugs
and defects, saving you time and effort you can instead spend
developing features for your users.
<
dependency>
<
groupId>
com.
atlassian.
commonmark<
/
groupId>
<
artifactId>
commonmark<
/
artifactId>
<
version>
0.13
.1
<
/
version>
<
/
dependency>
<
dependency>
<
groupId>
org.
jsoup<
/
groupId>
<
artifactId>
jsoup<
/
artifactId>
<
version>
1.13
.1
<
/
version>
<
/
dependency>
The Scala programming language gives you access to the vast Java ecosystem: runtimes, libraries, profilers, package repositories, all battle-tested and a single install away. No matter what you are building, with Scala you will find everything you need to take your idea to production.
Li Haoyi graduated from MIT with a degree in Computer Science and Engineering, and since then has been a major contributor to the Scala community. His open source projects have over 10,000 stars on Github, and are downloaded over 7,000,000 times a month. Haoyi has used Scala professionally to build distributed backend systems, programming languages, high-performance web applications, and much more.
Haoyi writes a blog about Scala and other technical topics at www.lihaoyi.com
1.1 Why Scala? | 15 |
1.2 Why This Book? | 16 |
1.3 How This Book Is Organized | 17 |
1.4 Code Snippet and Examples | 19 |
1.5 Online Materials | 21 |
package
app
object
MinimalApplication extends
cask.
MainRoutes {
@cask
.
get(
"/"
)
def
hello(
)
=
{
"Hello World!"
}
initialize(
)
}
</> 1.1.scala
Snippet 1.1: a tiny Scala web app, one of many example programs we will encounter in this book
Hands-on Scala teaches you how to use the Scala programming language in a practical, project-based fashion. Rather than trying to develop expertise in the deep details of the Scala language itself, Hands-on Scala aims to develop expertise using Scala in a broad range of practical applications. This book takes you from "hello world" to building interactive websites, parallel web crawlers, and distributed applications in Scala.
The book covers the concrete skills necessary for anyone using Scala professionally: handling files, data serializing, querying databases, concurrency, and so on. Hands-on Scala will guide you through completing several non-trivial projects which reflect the applications you may end up building as part of a software engineering job. This will let you quickly hit the ground running using Scala professionally.
2.1 Windows Setup (Optional) | 25 |
2.2 Installing Java | 25 |
2.3 Installing Ammonite | 26 |
2.4 Installing Mill | 30 |
2.5 IDE Support | 32 |
$ amm
Loading...
Welcome to the Ammonite Repl 2.2.0 (Scala 2.13.2 Java 11.0.7)
@ 1
+
1
res0:
Int
=
2
@ println(
"hello world"
+
"!"
*
10
)
hello world!
!
!
!
!
!
!
!
!
!
</> 2.1.scala
Snippet 2.1: getting started with the Ammonite Scala REPL
In this chapter, we will set up a simple Scala programming environment, giving you the ability to write, run, and test your Scala code. We will use this setup throughout the rest of the book. It will be a simple setup, but enough so you can get productive immediately with the Scala language.
Setting up your development environment is a crucial step in learning a new programming language. Make sure you get the setup in this chapter working. If you have issues, come to the online chat room https://www.handsonscala.com/chat to get help resolving them so you can proceed with the rest of the book in peace without tooling-related distractions.
3.1 Values | 39 |
3.2 Loops, Conditionals, Comprehensions | 46 |
3.3 Methods and Functions | 50 |
3.4 Classes and Traits | 53 |
for
(
i <-
Range.
inclusive(
1
,
100
)
)
{
println(
if
(
i %
3
==
0
&&
i %
5
==
0
)
"FizzBuzz"
else
if
(
i %
3
==
0
)
"Fizz"
else
if
(
i %
5
==
0
)
"Buzz"
else
i
)
}
</> 3.1.scala
Snippet 3.1: the popular "FizzBuzz" programming challenge, implemented in Scala
This chapter is a quick tour of the Scala language. For now we will focus on the basics of Scala that are similar to what you might find in any mainstream programming language.
The goal of this chapter is to get familiar you enough that you can take the same sort of code you are used to writing in some other language and write it in Scala without difficulty. This chapter will not cover more Scala-specific programming styles or language features: those will be left for Chapter 5: Notable Scala Features.
4.1 Operations | 59 |
4.2 Immutable Collections | 65 |
4.3 Mutable Collections | 71 |
4.4 Common Interfaces | 75 |
@ def
stdDev(
a:
Array[
Double
]
)
:
Double
=
{
val
mean =
a.
sum /
a.
length
val
squareErrors =
a.
map(
x =>
x -
mean)
.
map(
x =>
x *
x)
math.
sqrt(
squareErrors.
sum /
a.
length)
}
</> 4.1.scala
Snippet 4.1: calculating the standard deviation of an array using Scala Collection operations
The core of the Scala standard library is its collections: a common set of containers and data structures that are shared by all Scala programs. Scala's collections make it easy for you to manipulate arrays, linked lists, sets, maps and other data structures in convenient ways, providing built-in many of the data structures needed for implementing a typical application.
This chapter will walk through the common operations that apply to all collection types, before discussing the individual data structures and when you might use each of them in practice.
5.1 Case Classes and Sealed Traits | 81 |
5.2 Pattern Matching | 83 |
5.3 By-Name Parameters | 89 |
5.4 Implicit Parameters | 91 |
5.5 Typeclass Inference | 94 |
@ def
getDayMonthYear(
s:
String
)
=
s match
{
case
s"$day-$month-$year" =>
println(
s"found day: $day, month: $month, year: $year")
case
_ =>
println(
"not a date"
)
}
@ getDayMonthYear(
"9-8-1965"
)
found day:
9
,
month:
8
,
year:
1965
@ getDayMonthYear(
"9-8"
)
not a date
</> 5.1.scala
Snippet 5.1: using Scala's pattern matching feature to parse simple string patterns
This chapter will cover some of the more interesting and unusual features of Scala. For each such feature, we will cover both what the feature does as well as some common use cases to give you an intuition for what it is useful for.
Not every feature in this chapter will be something you use day-to-day. Nevertheless, even these less-commonly-used features are used often enough that it is valuable to have a high-level understanding for when you eventually encounter them in the wild.
6.1 Merge Sort | 107 |
6.2 Prefix Tries | 111 |
6.3 Breadth First Search | 117 |
6.4 Shortest Paths | 120 |
def
breadthFirstSearch[
T]
(
start:
T,
graph:
Map[
T,
Seq[
T]
]
)
:
Set[
T]
=
{
val
seen =
collection.
mutable.
Set(
start)
val
queue =
collection.
mutable.
ArrayDeque(
start)
while
(
queue.
nonEmpty)
{
val
current =
queue.
removeHead(
)
for
(
next <-
graph(
current)
if
!
seen.
contains(
next)
)
{
seen.
add(
next)
queue.
append(
next)
}
}
seen.
toSet
}
</> 6.1.scala
Snippet 6.1: a simple breadth-first-search algorithm we will implement using Scala in this chapter
In this chapter, we will walk you through the implementation of a number of common algorithms using the Scala programming language. These algorithms are commonly taught in schools and tested at professional job interviews, so you have likely seen them before.
By implementing them in Scala, we aim to get you more familiar with using the Scala programming language to solve small problems in isolation. We will also see how some of the unique language features we saw in Chapter 5: Notable Scala Features can be applied to simplify the implementation of these well-known algorithms. This will prepare us for subsequent chapters which will expand in scope to include many different kinds of systems, APIs, tools and techniques.
7.1 Paths | 127 |
7.2 Filesystem Operations | 129 |
7.3 Folder Syncing | 133 |
7.4 Simple Subprocess Invocations | 137 |
7.5 Interactive and Streaming Subprocesses | 141 |
@ os.
walk(
os.
pwd)
.
filter(
os.
isFile)
.
map(
p =>
(
os.
size(
p)
,
p)
)
.
sortBy(
-
_.
_1)
.
take(
5
)
res60:
IndexedSeq[
(
Long
,
os.
Path)
]
=
ArrayBuffer(
(
6340270L
,
/
Users/
lihaoyi/
test/
post/
Reimagining/
GithubHistory.
gif)
,
(
6008395L
,
/
Users/
lihaoyi/
test/
post/
SmartNation/
routes.
json)
,
(
5499949L
,
/
Users/
lihaoyi/
test/
post/
slides/
Why-
You-
Might-
Like-
Scala.
js.
pdf)
,
(
5461595L
,
/
Users/
lihaoyi/
test/
post/
slides/
Cross-
Platform-
Development-
in-
Scala.
js.
pdf)
,
(
4576936L
,
/
Users/
lihaoyi/
test/
post/
Reimagining/
FluentSearch.
gif)
)
</> 7.1.scala
Snippet 7.1: a short Scala code snippet to find the five largest files in a directory tree
Working with files and subprocesses is one of the most common things you do in programming: from the Bash shell, to Python or Ruby scripts, to large applications written in a compiled language. At some point everyone will have to write to a file or talk to a subprocess. This chapter will walk you through how to perform basic file and subprocess operations in Scala.
This chapter finishes with two small projects: building a simple file synchronizer, and building a streaming subprocess pipeline. These projects will form the basis for Chapter 17: Multi-Process Applications and Chapter 18: Building a Real-time File Synchronizer
8.1 Manipulating JSON | 147 |
8.2 JSON Serialization of Scala Data Types | 150 |
8.3 Writing your own Generic Serialization Methods | 154 |
8.4 Binary Serialization | 157 |
@ val
output =
ujson.
Arr(
ujson.
Obj(
"hello"
->
"world"
,
"answer"
->
42
)
,
true
)
@ output(
0
)
(
"hello"
)
=
"goodbye"
@ output(
0
)
(
"tags"
)
=
ujson.
Arr(
"awesome"
,
"yay"
,
"wonderful"
)
@ println(
output)
[
{
"hello"
:
"goodbye"
,
"answer"
:
42
,
"tags"
:
[
"awesome"
,
"yay"
,
"wonderful"
]
}
,
true
]
</> 8.1.scala
Snippet 8.1: manipulating a JSON tree structure in the Scala REPL
Data serialization is an important tool in any programmer's toolbox. While variables and classes are enough to store data within a process, most data tends to outlive a single program process: whether saved to disk, exchanged between processes, or sent over the network. This chapter will cover how to serialize your Scala data structures to two common data formats - textual JSON and binary MessagePack - and how you can interact with the structured data in a variety of useful ways.
The JSON workflows we learn in this chapter will be used later in Chapter 12: Working with HTTP APIs and Chapter 14: Simple Web and API Servers, while the binary serialization techniques we learn here will be used later in Chapter 17: Multi-Process Applications.
9.1 Reading Files Off Disk | 163 |
9.2 Rendering HTML with Scalatags | 164 |
9.3 Rendering Markdown with Commonmark-Java | 166 |
9.4 Links and Bootstrap | 170 |
9.5 Optionally Deploying the Static Site | 174 |
os.
write(
os.
pwd /
"out"
/
"index.html"
,
doctype(
"html"
)
(
html(
body(
h1(
"Blog"
)
,
for
(
(
_,
suffix,
_)
<-
postInfo)
yield
h2(
a(
href :
=
(
"post/"
+
mdNameToHtml(
suffix)
)
)
(
suffix)
)
)
)
)
)
</> 9.1.scala
Snippet 9.1: rendering a HTML page using the third-party Scalatags HTML library
Scala Scripts are a great way to write small programs. Each script is self-contained and can download its own dependencies when necessary, and make use of both Java and Scala libraries. This lets you write and distribute scripts without spending time fiddling with build configuration or library installation.
In this chapter, we will write a static site generator script that uses third-party libraries to process Markdown input files and generate a set of HTML output files, ready for deployment on any static file hosting service. This will form the foundation for Chapter 10: Static Build Pipelines, where we will turn the static site generator into an efficient incremental build pipeline by using the Mill build tool.
10.1 Mill Build Pipelines | 179 |
10.2 Mill Modules | 183 |
10.3 Revisiting our Static Site Script | 187 |
10.4 Conversion to a Mill Build Pipeline | 188 |
10.5 Extending our Static Site Pipeline | 192 |
import
mill.
_
def
srcs =
T.
source(
millSourcePath /
"src"
)
def
concat =
T{
os.
write(
T.
dest /
"concat.txt"
,
os.
list(
srcs(
)
.
path)
.
map(
os.
read(
_)
)
)
PathRef(
T.
dest /
"concat.txt"
)
}
</> 10.1.scala
Snippet 10.1: the definition of a simple Mill build pipeline
Build pipelines are a common pattern, where you have files and assets you want to process but want to do so efficiently, incrementally, and in parallel. This usually means only re-processing files when they change, and re-using the already processed assets as much as possible. Whether you are compiling Scala, minifying Javascript, or compressing tarballs, many of these file-processing workflows can be slow. Parallelizing these workflows and avoiding unnecessary work can greatly speed up your development cycle.
This chapter will walk through how to use the Mill build tool to set up these build pipelines, and demonstrate the advantages of a build pipeline over a naive build script. We will take the the simple static site generator we wrote in Chapter 9: Self-Contained Scala Scripts and convert it into an efficient build pipeline that can incrementally update the static site as you make changes to the sources. We will be using the Mill build tool in several of the projects later in the book, starting with Chapter 14: Simple Web and API Servers.
11.1 Scraping Wikipedia | 203 |
11.2 MDN Web Documentation | 207 |
11.3 Scraping MDN | 209 |
11.4 Putting it Together | 213 |
@ val
doc =
Jsoup.
connect(
"http://en.wikipedia.org/"
)
.
get(
)
@ doc.
title(
)
res2:
String
=
"Wikipedia, the free encyclopedia"
@ val
headlines =
doc.
select(
"#mp-itn b a"
)
headlines:
select.
Elements =
<
a href=
"/wiki/Bek_Air_Flight_2100"
title=
"Bek Air Flight 2100"
>
Bek Air Flight 2100
<
/
a>
<
a href=
"/wiki/Assassination_of_..."
title=
"Assassination of ..."
>
2018
killing<
/
a>
<
a href=
"/wiki/State_of_the_..."
title=
"State of the..."
>
upholds a ruling<
/
a>
.
.
.
</> 11.1.scala
Snippet 11.1: scraping Wikipedia's front-page links using the Jsoup third-party library in the Scala REPL
The user-facing interface of most networked systems is a website. In fact, often that is the only interface! This chapter will walk you through using the Jsoup library from Scala to scrape human-readable HTML pages, unlocking the ability to extract data from websites that do not provide access via an API.
Apart from third-party scraping websites, Jsoup is also a useful tool for testing the HTML user interfaces that we will encounter in Chapter 14: Simple Web and API Servers. This chapter is also a chance to get more familiar with using Java libraries from Scala, a necessary skill to take advantage of the broad and deep Java ecosystem. Lastly, it is an exercise in doing non-trivial interactive development in the Scala REPL, which is a great place to prototype and try out pieces of code that are not ready to be saved in a script or project.
12.1 The Task: Github Issue Migrator | 219 |
12.2 Creating Issues and Comments | 221 |
12.3 Fetching Issues and Comments | 223 |
12.4 Migrating Issues and Comments | 228 |
@ requests.
post(
"https://api.github.com/repos/lihaoyi/test/issues"
,
data =
ujson.
Obj(
"title"
->
"hello"
)
,
headers =
Map(
"Authorization"
->
s"token $token")
)
res1:
requests.
Response =
Response(
"https://api.github.com/repos/lihaoyi/test/issues"
,
201
,
"Created"
,
.
.
.
</> 12.1.scala
Snippet 12.1: interacting with Github's HTTP API from the Scala REPL
HTTP APIs have become the standard for any organization that wants to let external developers integrate with their systems. This chapter will walk you through how to access HTTP APIs in Scala, building up to a simple use case: migrating Github issues from one repository to another using Github's public API.
We will build upon techniques learned in this chapter in Chapter 13: Fork-Join Parallelism with Futures, where we will be writing a parallel web crawler using the Wikipedia JSON API to walk the graph of articles and the links between them.
13.1 Parallel Computation using Futures | 237 |
13.2 N-Ways Parallelism | 240 |
13.3 Parallel Web Crawling | 243 |
13.4 Asynchronous Futures | 248 |
13.5 Asynchronous Web Crawling | 252 |
def
fetchAllLinksParallel(
startTitle:
String
,
depth:
Int
)
:
Set[
String
]
=
{
var
seen =
Set(
startTitle)
var
current =
Set(
startTitle)
for
(
i <-
Range(
0
,
depth)
)
{
val
futures =
for
(
title <-
current)
yield
Future{
fetchLinks(
title)
}
val
nextTitleLists =
futures.
map(
Await.
result(
_,
Inf)
)
current =
nextTitleLists.
flatten.
filter(
!
seen.
contains(
_)
)
seen =
seen ++
current
}
seen
}
</> 13.1.scala
Snippet 13.1: a simple parallel web-crawler implemented using Scala Futures
The Scala programming language comes with a Futures API. Futures make parallel and asynchronous programming much easier to handle than working with traditional techniques of threads, locks, and callbacks.
This chapter dives into Scala's Futures: how to use them, how they work, and how you can use them to parallelize data processing workflows. It culminates in using Futures together with the techniques we learned in Chapter 12: Working with HTTP APIs to write a high-performance concurrent web crawler in a straightforward and intuitive way.
14.1 A Minimal Webserver | 259 |
14.2 Serving HTML | 263 |
14.3 Forms and Dynamic Data | 265 |
14.4 Dynamic Page Updates via API Requests | 272 |
14.5 Real-time Updates with Websockets | 276 |
object
MinimalApplication extends
cask.
MainRoutes {
@cask
.
get(
"/"
)
def
hello(
)
=
{
"Hello World!"
}
@cask
.
post(
"/do-thing"
)
def
doThing(
request:
cask.
Request)
=
{
request.
text(
)
.
reverse
}
initialize(
)
}
</> 14.1.scala
Snippet 14.1: a minimal Scala web application, using the Cask web framework
Web and API servers are the backbone of internet systems. While in the last few chapters we learned to access these systems from a client's perspective, this chapter will teach you how to provide such APIs and Websites from the server's perspective. We will walk through a complete example of building a simple real-time chat website serving both HTML web pages and JSON API endpoints. We will re-visit this website in Chapter 15: Querying SQL Databases, where we will convert its simple in-memory datastore into a proper SQL database.
15.1 Setting up Quill and PostgreSQL | 285 |
15.2 Mapping Tables to Case Classes | 287 |
15.3 Querying and Updating Data | 290 |
15.4 Transactions | 295 |
15.5 A Database-Backed Chat Website | 297 |
@ ctx.
run(
query[
City]
.
filter(
_.
population >
5000000
)
.
filter(
_.
countryCode ==
"CHN"
)
)
res16:
List[
City]
=
List(
City(
1890
,
"Shanghai"
,
"CHN"
,
"Shanghai"
,
9696300
)
,
City(
1891
,
"Peking"
,
"CHN"
,
"Peking"
,
7472000
)
,
City(
1892
,
"Chongqing"
,
"CHN"
,
"Chongqing"
,
6351600
)
,
City(
1893
,
"Tianjin"
,
"CHN"
,
"Tianjin"
,
5286800
)
)
</> 15.1.scala
Snippet 15.1: using the Quill database query library from the Scala REPL
Most modern systems are backed by relational databases. This chapter will walk you through the basics of using a relational database from Scala, using the Quill query library. We will work through small self-contained examples of how to store and query data within a Postgres database, and then convert the interactive chat website we implemented in Chapter 14: Simple Web and API Servers to use a Postgres database for data storage.
16.1 Castor Actors | 307 |
16.2 Actor-based Background Uploads | 308 |
16.3 Concurrent Logging Pipelines | 314 |
16.4 Debugging Actors | 321 |
class
SimpleUploadActor(
)
(
implicit
cc:
castor.
Context)
extends
castor.
SimpleActor[
String
]
{
def
run(
msg:
String
)
=
{
val
res =
requests.
post(
"https://httpbin.org/post"
,
data =
msg)
println(
"response "
+
res.
statusCode)
}
}
</> 16.1.scala
Snippet 16.1: a simple actor implemented in Scala using the Castor library
Message-based parallelism is a technique that involves splitting your application logic into multiple "actors", each of which can run concurrently, and only interacts with other actors by exchanging asynchronous messages. This style of programming was popularized by the Erlang programming language and the Akka Scala actor library, but the approach is broadly useful and not limited to any particular language or library.
This chapter will introduce the fundamental concepts of message-based parallelism with actors, and how to use them to achieve parallelism in scenarios where the techniques we covered in Chapter 13: Fork-Join Parallelism with Futures cannot be applied. We will first discuss the basic actor APIs, see how they can be used in a standalone use case, and then see how they can be used in more involved multi-actor pipelines. The techniques in this chapter will come in useful later in Chapter 18: Building a Real-time File Synchronizer.
17.1 Two-Process Build Setup | 327 |
17.2 Remote Procedure Calls | 330 |
17.3 The Agent Process | 332 |
17.4 The Sync Process | 334 |
17.5 Pipelined Syncing | 337 |
def
send[
T:
Writer]
(
out:
DataOutputStream,
msg:
T)
:
Unit
=
{
val
bytes =
upickle.
default.
writeBinary(
msg)
out.
writeInt(
bytes.
length)
out.
write(
bytes)
out.
flush(
)
}
def
receive[
T:
Reader]
(
in:
DataInputStream)
=
{
val
buf =
new
Array[
Byte
]
(
in.
readInt(
)
)
in.
readFully(
buf)
upickle.
default.
readBinary[
T]
(
buf)
}
</> 17.1.scala
Snippet 17.1: RPC send and receive methods for sending data over an operating system pipe or network
While all our programs so far have run within a single process, in real world scenarios you will be working as part of a larger system, and the application itself may need to be split into multiple processes. This chapter will walk you through how to do so: configuring your build tool to support multiple Scala processes, sharing code and exchanging serialized messages. These are the building blocks that form the foundation of any distributed system.
As this chapter's project, we will be building a simple multi-process file synchronizer that can work over a network. This chapter builds upon the simple single-process file synchronizer in Chapter 7: Files and Subprocesses, and will form the basis for Chapter 18: Building a Real-time File Synchronizer.
18.1 Watching for Changes | 345 |
18.2 Real-time Syncing with Actors | 346 |
18.3 Testing the Syncer | 353 |
18.4 Pipelined Real-time Syncing | 355 |
18.5 Testing the Pipelined Syncer | 358 |
object
SyncActor extends
castor.
SimpleActor[
Msg]
{
def
run(
msg:
Msg)
:
Unit
=
msg match
{
case
ChangedPath(
value)
=>
Shared.
send(
agent.
stdin.
data,
Rpc.
StatPath(
value)
)
case
AgentResponse(
Rpc.
StatInfo(
p,
remoteHash)
)
=>
val
localHash =
Shared.
hashPath(
src /
p)
if
(
localHash !=
remoteHash &&
localHash.
isDefined)
{
Shared.
send(
agent.
stdin.
data,
Rpc.
WriteOver(
os.
read.
bytes(
src /
p)
,
p)
)
}
}
}
</> 18.1.scala
Snippet 18.1: an actor used as part of our real-time file synchronizer
In this chapter, we will write a file synchronizer that can keep the destination folder up to date even as the source folder changes over time. This chapter serves as a capstone project, tying together concepts from Chapter 17: Multi-Process Applications and Chapter 16: Message-based Parallelism with Actors.
The techniques in this chapter form the basis for "event driven" architectures, which are common in many distributed systems. Real-time file synchronization is a difficult problem, and we will see how we can use the Scala language and libraries to approach it in an elegant and understandable way.
19.1 Simple Parsers | 363 |
19.2 Parsing Structured Values | 368 |
19.3 Implementing a Calculator | 372 |
19.4 Parser Debugging and Error Reporting | 377 |
@ def
parser[
_:
P]
=
P(
(
"hello"
|
"goodbye"
)
.
! ~
" "
.
rep(
1
)
~
(
"world"
|
"seattle"
)
.
! ~
End )
@ fastparse.
parse(
"hello seattle"
,
parser(
_)
)
res41:
Parsed[
(
String
,
String
)
]
=
Success(
(
"hello"
,
"seattle"
)
,
13
)
@ fastparse.
parse(
"hello world"
,
parser(
_)
)
res42:
Parsed[
(
String
,
String
)
]
=
Success(
(
"hello"
,
"world"
)
,
15
)
</> 19.1.scala
Snippet 19.1: parsing simple text formats using the FastParse library
One common programming task is parsing structured text. This chapter will introduce how to parse text in Scala using the FastParse library, before diving into an example where we write a simple arithmetic parser in Scala. This will allow you to work competently with unusual data formats, query languages, or source code for which you do not already have an existing parser at hand.
We will build upon the parsing techniques learned in this chapter as part of Chapter 20: Implementing a Programming Language.
20.1 Interpreting Jsonnet | 385 |
20.2 Jsonnet Language Features | 385 |
20.3 Parsing Jsonnet | 387 |
20.4 Evaluating the Syntax Tree | 396 |
20.5 Serializing to JSON | 402 |
def
evaluate(
expr:
Expr,
scope:
Map[
String
,
Value]
)
:
Value =
expr match
{
case
Expr.
Str(
s)
=>
Value.
Str(
s)
case
Expr.
Dict(
kvs)
=>
Value.
Dict(
kvs.
map{
case
(
k,
v)
=>
(
k,
evaluate(
v,
scope)
)
}
)
case
Expr.
Plus(
left,
right)
=>
val
Value.
Str(
leftStr)
=
evaluate(
left,
scope)
val
Value.
Str(
rightStr)
=
evaluate(
right,
scope)
Value.
Str(
leftStr +
rightStr)
}
</> 20.1.scala
Snippet 20.1: evaluating a syntax tree using pattern matching
This chapter builds upon the simple parsers we learned in Chapter 19: Parsing Structured Text, and walks you through the process of implementing a simple programming language in Scala.
Working with programming language source code is a strength of Scala: parsing, analyzing, compiling, or interpreting it. This chapter should will you how easy it is to write a simple interpreter to parse and evaluate program source code in Scala. Even if your goal is not to implement an entirely new programming language, these techniques are still useful: for writing linters, program analyzers, query engines, and other such tools.
The chapters of Hands-on Scala are broken down into four parts:
Each part of the book focuses on one particular aspect of using the Scala language. The chapters within each part build up to one or more projects that make use of what you learned throughout the preceding chapters.
The diagram on the left illustrates how the chapters are organized: which chapters depend on each other as prerequisites, and which chapters are independent. You can use this to chart your own path through Hands-on Scala, focusing your attention on the chapters that cover topics you are most interested in.
@ val
myList =
List(
1
,
2
,
3
,
4
,
5
)
myList:
List[
Int
]
=
List(
1
,
2
,
3
,
4
,
5
)
@ val
myTail =
myList.
tail
myTail:
List[
Int
]
=
List(
2
,
3
,
4
,
5
)
@ val
myOtherList =
0
:
:
myList
myOtherList:
List[
Int
]
=
List(
0
,
1
,
2
,
3
,
4
,
5
)
@ val
myThirdList =
-
1
:
:
myList
myThirdList:
List[
Int
]
=
List(
-
1
,
1
,
2
,
3
,
4
,
5
)
The first 5 chapters of Hands-on Scala are free to read, as a standalone introduction to the Scala language. This is an excellent way to learn the basics of the Scala language whether or not you are intending on purchasing the book.
Introduction to Scala is available online, or as free PDF, EPub or Mobi downloads. It takes you through setting up, basic language constructs, the Scala standard collections library, and finally lets you use Scala to implement some fun, well-known algorithms. If you are curious about the Scala language or curious about the kind of content that is in this book, feel free to start reading!
Hands-on Scala has a free online repository of supporting materials, including more than 120 self-contained executable code examples. This repository forms a great reference for you to quickly look up examples of working code to accomplish common tasks in Scala.
These examples cover everything from basic Scala syntax and standard library
APIs, to simple filesystem operations, database access, HTTP servers and
clients, programming language interpreters, and distributed file synchronizers.
Each example is self-contained, and can be run and tested using the commands
provided in its
readme.md
. These form a great reference cookbook for anyone using Scala, whether
or not they read the book, although following along with
Hands-on Scala
will give you the best experience and help you get the most out of them.
You don't need to go alone! Hands-on Scala has online discussion threads for every chapter, and a chat room for more interactive discussions. Get help from the author or compare notes with fellow learners, so you never need to get stuck. Hands-on Scala's online community of learners helps enrich your learning experience far beyond that of other books or tutorials.