Scala Functional Programming Patterns - Sample Chapter
Scala Functional Programming Patterns - Sample Chapter
$ 49.99 US
31.99 UK
P U B L I S H I N G
Atul S. Khot
Scala Functional
Programming Patterns
ee
pl
C o m m u n i t y
E x p e r i e n c e
D i s t i l l e d
Scala Functional
Programming Patterns
Grok and perform effective functional programming in Scala
Sa
m
Atul S. Khot
works. From there, he moved on to writing a lot of C++ code and then moved further
to Java and Scala. He is an avid open source advocate who loves scripting languages
and clean coding. He is ever ready to learn a new command-line trick. Atul currently
works at Webonise Labs, Pune. He was also a panelist for Dr. Dobb's Jolt Awards.
Last but not least, he is a trekking enthusiast and also a big foodie.
Preface
This is a book on functional programming patterns using Scala. Functional
programming uses functions as basic building blocks. These are functions that don't
have any side effects. This challenges our notions about how to write programs. The
order of execution for such functions does not matter. We get to reason about them
in a referentially transparent manner. This can be a big help in proving correctness.
It feels just like a plain arithmetic problem, where (2+2)*(3+3) always equals 24. You
may evaluate the expression as 2+2 first, or as 3+3.
I got to know about the Unix culture early in my career. The Unix philosophy relies
on pipelining small programs, each doing functionally one and only one thing. One
can connect these processing nuggets together. In addition to these hundreds of
ready-made building blocks, you could write your own too. These could be easily
connected in the pipeline. These pipes and filters were a deeply influential concept as
a whole. When I saw Scala's combinators, options, and for comprehensions, I knew I
was looking at pipes and filters again. The nuggets in this case were Scala functions
instead of Unix processes. Understanding functional programming gives you a
new perspective on your code and programming in general. The other aspect of
pipelining is that you tend to reuse them intuitively, and you also write less. Though
you iterate lines of a text file in a Unix shell pipeline, you don't write any for loops.
These are done for you. You just specify which lines pass your criteria or how to
transform these lines or both.
Preface
Scala allows you to do just thatalbeit in a somewhat different form. You don't need
to write a for loop, and you keep away from loop counters. Instead, the language
invites you to write for comprehensions. Immutability is an actively advocated rule of
thumb. Another is avoiding side effects. Scala advocates both. As you probably know,
immutability paves the way for more robust concurrency. Why are these so important?
Simple, we need to reason about code. Any strategy that makes this activity controlled
and simpler is a godsend! Does going down the immutable route mean we end up
doing too much copying? How would this Copy On Write measure up against large
data structures? Scala has an answer for this in the form of structural sharing.
One-liners are very popular as they get a lot done in a line of code. Scala features
allow you to compose such one-liners. Instead of reiterating the same collection, you
can do it in one elegant expression. For example, creating an immutable class with
constructor and equality comparison defined that is bestowed with destructuring
powers is just a one-liner. We just define a case class. There are many situations
where Scala one-liners save a lot of programmer time and result in far less code.
Combinators such as map, flatMap, filter, and foreach are composed together to
express a lot of logic in a one-liner. How does it all affect a program design and
design patterns? Here are a few illustrative cases. The singleton design pattern
is used to ensure that only one instance of a class could ever exist. Null Objects
are specialized singletons that are used to avoid nulls and null checks. Scala's
Options give us a similar functionality. Scala's object keyword gives us ready-made
singletons. Singletons are specialized factories. A factory creates objects. Scala's
syntactic sugar give us a very succinct way to use the apply factory method.
The command design pattern encapsulates an object as a command. It invokes a
method on the object. Scala has parameters by name. These are not evaluated at
the call site but instead are evaluated at each use within the function. This feature
effectively replaces the command pattern with first-class language support. The
strategy pattern encapsulates algorithms and allows us to select one at runtime.
In Java, we could express the strategy as an interface and the varying algorithms
as concrete implementations. Scala's functions are first-class objects. You can pass
functions around as method arguments and return values. Functions can be very
effective substitutes for the strategy pattern. The ability to define anonymous
functions is really helpful here. The Decorator pattern is needed at times. It can be
used to decorate (that is, extend) the functionality of an object without modifying
it. Some design plumbing needs to be done though. Scala's stackable traits can
express the same design very elegantly. One use of the proxy design pattern is for
implementing lazy evaluation. When some computation is expensive, we do it only
when needed.
Preface
As we are very familiar with eager evaluations, we create a list in memory and think
it is fully realized. However, just like eager lists, there are lazy lists too. If we think
of a typical OR (||) conditional statement, if the left operand is true, the right is not
evaluated. This is a very powerful concept. Scala's streams provide lazy lists.
Chain of responsibility is another handy pattern that is used to decouple the sender
of a request from its receiver and allows more than one object (a chain of objects) to
handle a request. If any object in the chain is not able to handle the request, it passes
the request to its next object in the chain. Scala's partial functions fit this bill nicely.
We can chain partial functions with Scala's orElse operator to realize the pattern.
When we write code, we need to handle errors. Scala's Try/Success/Failure again
allows us to write pipelines, and if any piece of the pipeline is an error, rest of the
pipeline processing is skipped.
We will look at all these concepts in greater detail. We will set up a problem, look at
the traditional Java solution, and then see how Scala changes the game.
Welcome to the Scala wonderland!
Preface
Chapter 7, Of Visitors and Chains of Responsibilities, covers the Visitor pattern and
its application. The other topics that are discussed are Scala's pattern matching
capabilities and the chain of responsibility pattern. We will also learn Scala
implementation using orElse and the collect idiom.
Chapter 8, Traversals Mapping/Filtering/Folding/Reducing, covers iterators and
functional iteration using map, filter, fold, and reduce. This chapter introduces
Monads and explains ReduceLeft and ReduceRight.
Chapter 9, Higher Order Functions, discusses the strategy pattern and Scala version
using higher order functions. It covers map as a functor, flatMap as a Monad, and
foldLeft as Monoids. Here, you will also learn how to iterate lazy collections.
Chapter 10, Actors and Message Passing, showcases a case study to recursively grep a
directory for files that contain matching text. It also covers the producer consumer
pattern and the Master Slave pattern. It explains the concept of poison pills, eventdriven programming, immutability, and concurrency. It also talks about Akka and
Actors and how to reimplement recursive grep using Actors.
Chapter 11, It's a Paradigm Shift, teaches you how to sort in Scala and the
Schwarzian transform implemented in Scala. It discusses functional error handling
with Try/Success/Failure. And talks about Java Threads versus Scala's Futures.
Scala's Parser Combinators are also discussed here.
[1]
Finally, we will look at idioms and patterns. Traditional patterns in Scala look very
different from their Java counterparts. We will briefly look at the command and
strategy of Scala and see how functions are used to pass algorithms around.
Welcome to the Scala fun ride!
Abstractions
What do we mean by abstractions? Why are they important? To understand this,
we will compare two approaches. First, the "go to the wall and pull at one of the
wooden panels fitted into the rectangular hole" approach versus the "open the
door, please" approach.
A door is an abstraction. We really don't care whether it is made of wood or some
other material, or what type of a lock it has, or any other details. The details are
important, but not always. If we worry about the details always, we would quickly
get bogged down, and all the communication would effectively come to a halt.
A table is an abstraction, so is a chair, and so is a house. I hope you get the drift.
We want to be selectively ignorant of the details at times, and selective ignorance
is abstraction.
Now, you may ask why does it matter to us programmers? The reason is that it gets
things done in a compact manner.
For example, let's look at the following Scala code, which is used to count the
frequency of characters in a string:
scala> "hello world".toList.filter(_.isLetter).groupBy(x => x).map { y =>
|
| }
res1: scala.collection.immutable.Map[Char,Int] = Map(e -> 1, l -> 3, h ->
1, r -> 1, w -> 1, o -> 2, d -> 1)
Isn't it compact?
On the Urban Dictionary website, http://www.urbandictionary.com/define.
php?term=cutie, the term "cutie" is defined as compact beautythe kind you just
want to put in your pocket and keep beside you forever.
I was bowled over when I first saw it all. It is concise, short, packs a punch, and is
elegant. The Type Less Do More philosophy. Gets a lot done with less...
[2]
Chapter 1
To run the code snippet, fire up the Scala REPL. This looks a lot like
a command console, a prompt waiting for you to key in Scala code.
In a command terminal, typing just Scala fires up the REPL:
~> scala
It will give the following output:
Welcome to Scala version 2.11.7 (Java HotSpot(TM)
64-Bit Server VM, Java 1.8.0_25).
For me, Scala brought the thrill back to programming... You can do a great deal by
writing a few lines of codeless is more...
Scala gives you many tools, so the code is abstract and reusable...
Concise expression
You always want to be able to concisely express yourself. Let's try the following
command to create a string:
scala> val list = List("One", "two", "three", "Four", "five")
list: List[String] = List(One, two, three, Four, five)
We have created a list of strings. Note that we neither had to specify the type of the
list for this, nor any new keyword. We just expressed that we wanted a list assigned
to a read-only variable.
Code reviews do a lot of good to a code base. I keep looking for places where
I can replace a variable assignment with variable initialization. Refer to the
following URL for an example: http://www.refactoring.com/catalog/
replaceAssignmentWithInitialization.html.
[3]
Scala helps us with the val keyword. With the use of this keyword, we can establish
the following:
The initial value of variable must be specified (it is impossible for the variable
to remain uninitialized).
The value of variable cannot ever be changed again (there is one less
moving part).
Why is this so important? A system with less moving parts is easier to understand
and explain. You will be knowing that Google is well known for its less-moving-parts
software. Let's try the following command to check for the uppercase in these characters:
scala> def hasUpperCaseChar(s: String) = s.exists(_.isUpper)
hasLowerCaseChar: (s: String)Boolean
What does s.exists(_.isUpper) do? In this code, I have a string and I am checking
whether it has one or more uppercase characters.
Note that I need to look at each character of the string to arrive at an answer as
output. However, I did not have to split the string into its constituent characters and
then work on each character.
I am just expressing the algorithm. The algorithm involves iterating all characters.
However, I did not write a loop. Instead, I expressed what I meant, concisely. Scala's
strings are collections of characters. We can try the following command to make use
of a filter method:
scala> list filter (hasUpperCaseChar)
res2: List[String] = List(One, Four)
Just like a string, List is again a collection, in this case, of strings. I used a list
method, filter, to weed out elements that did not satisfy the predicate.
If we were to write this imperatively, we would need a nested loop (a loop within
another loop). The first loop would take each string, and the inner loop would work
on each character of the string. We would need a list to collect the matching elements.
Instead of lists, we just declared what we wanted to happen. However, at some point
of time in the code the looping needs to happen! It does happen indeed, but behind
the scenes and in the filter method.
The filter method is a higher order function that receives the hasUpperCaseChar
function.
[4]
Chapter 1
Let's say, in addition to this method, we want only those string elements that have a
length greater than 3:
scala> list filter (x => hasLowerCaseChar(x) && x.size > 3)
res1: List[String] = List(Four)
We are again executing the algorithm; however, with a different match criteria. We
are passing a function in the form of a function literal. Each element in the list is
bound to x, and we run a check on x.
The preceding form of expression allows us to concisely express our intent. A large
part of this flexibility comes from the idea of sending small computations around
that are expressible without much ado. Welcome to functions!
Functions
Functional programming includes a lot about functions. There are different kinds of
functions in programming, for example, pure functions. A pure function depends
only on its input to compute the output. Let's try the following example to make use
of functions:
scala> val addThem = (x: Int, y: Int) => x + y + 1
addThem: (Int, Int) => Int = <function2>
scala> addThem(3,4)
res2: Int = 8
As long as the function lives, it will always give the result 8 given the input (3,4).
Take a look at the following example of a pure function:
Pure
function
x=3
x+y+1
y=4
[5]
The functions worked on the input and produced the output, without changing any
state. What does the phrase "did not change any state" mean? Here is an example of a
not-so-pure function:
scala> var p = 1
p: Int = 1
scala> val addP = (x: Int, y: Int) => {
| p += 1
| x + y + p
| }
addP: (Int, Int) => Int = <function2>
scala> addP(3, 4)
res4: Int = 9
scala> addP(3, 4)
res5: Int = 10
This addP function changes the worldthis means that it affects its surroundings. In
this case, the variable p. Here is the diagrammatic representation for the preceding code:
P
value depends on
how many times mutated
impure
function
Changes
p+=1
x+y +p
x=3
???
depends on
value of p
y=4
Comparing addThem and addP, which of the two is clearer to reason about?
Remember that while debugging, we look for the trouble spot, and we wish to find it
quickly. Once found, we can fix the problem quickly and keep things moving.
For the pure function, we can take a paper and pen, and since we know that it is side
effects free, we can write the following:
addThem(3, 4) = 8
addThem(1,1) = 3
[6]
Chapter 1
For small numbers, we can do the function computation in our heads. We can even
replace the function call with the value:
scala> addThem(1,1) + addThem(3,4)
res10: Int = 11
scala> 3 + 8
res11: Int = 11
Both the preceding expressions are equivalent. When we replace the function, we
deal with referentially transparent expressions. If the function is a long running one,
we could call it just once and cache the results. The cached results would be used for
the second and subsequent calls.
The addP function, on the other hand, is referentially opaque.
Immutable
Each one of us has a name. Let's keep this simplea first and last name. My first
name is Atul and my last name is Khot. If someone suddenly called me by the name
Prakash, things won't work!
Keeping aside cases such as writers taking a pen name (that is, Plum for PG
Wodehouse), commonly each one of us has a standard, official name. We simply
don't want parts of it changed to willy nilly. Let's try the following example:
scala> case class FullName(firstName: String, lastName: String)
defined class FullName
scala> val name = FullName("Bertie", "Wooster")
name: FullName = FullName(Bertie,Wooster)
Scala stopped us changing the code of Woosters!! It just saved Bertie from getting
a wife!
In case you need a break and some light relief, Google The Code of the Woosters!
[7]
Once a case class instance is created, it is sealed. You can read it, but you cannot
change it:
scala> name.firstName
res12: String = Bertie
scala> name.lastName
res13: String = Wooster
You can even look at the signified version of the instance that the compiler writes
for you:
scala> name
res14: FullName = FullName(Bertie,Wooster)
And you can destructure it using pattern matching. Immutability just reduces the
moving parts and helps us to restore sanity. This is a boon when threads enter the
picture.
Referential transparency
To understand referential transparency, let's first consider the following description:
Let me tell you a bit about India's capital, New Delhi. The Indian capital houses the
Indian Parliament. The Indian capital is also home to Gali Paranthe Wali, where you
get to eat the famous parathas.
We can also say the following instead:
Let me tell you a bit about India's capital, New Delhi. New Delhi houses the Indian
Parliament. New Delhi is also home to Gali Paranthe Wali, where you get to eat the
famous parathas.
Here, we substituted New Delhi with the Indian capital, but the meaning did not
change. This is how we would generally express ourselves.
The description is referentially transparent with the following commands:
scala> def f1(x: Int, y: Int) = x * y
f1: (x: Int, y: Int)Int
scala> def f(x: Int, y: Int, p: Int, q: Int)= x * y + p * q
f: (x: Int, y: Int, p: Int, q: Int)Int
scala> f(2, 3, 4, 5)
res0: Int = 26
[8]
Chapter 1
The f1 method just depends upon its arguments, that is, it is pure.
Which method is not referentially transparent? Before we look at an example, let's
look at Scala's ListBuffer function:
scala> import scala.collection.mutable.ListBuffer
import scala.collection.mutable.ListBuffer
The ListBuffer is a mutable collection. You can append a value to the buffer and
modify it in place:
scala> val v = ListBuffer.empty[String]
v: scala.collection.mutable.ListBuffer[String] = ListBuffer()
scala> v += "hello"
res10: v.type = ListBuffer(hello)
scala> v
res11: scala.collection.mutable.ListBuffer[String] = ListBuffer(hello)
scala> v += "world"
res12: v.type = ListBuffer(hello, world)
scala> v
res13: scala.collection.mutable.ListBuffer[String] = ListBuffer(hello,
world)
Armed with this knowledge, let's now look at the following command:
scala> val lb = ListBuffer(1, 2)
lb: scala.collection.mutable.ListBuffer[Int] = ListBuffer(1, 2)
scala> val x = lb += 9
x: lb.type = ListBuffer(1, 2, 9)
scala> println(x.mkString("-"))
[9]
However, by substituting x with the expression (lb += 9), we get the following:
scala> println((lb += 9).mkString("-")) // 1
1-2-9-9
scala> println((lb += 9).mkString("-")) // 2
1-2-9-9-9
[ 10 ]
Chapter 1
new MyPredicate());
assertThat(groups, emptyIterable());
}
@Test
public void testOnlyOneGroup() {
List<Integer> list = Lists.newArrayList(1);
List<List<Integer>> groups = groupThem.groupThem(list,
new MyPredicate());
assertThat(groups.size(), equalTo(1));
assertThat(groups.get(0), contains(1));
}
We will make use of the excellent Hamcrest matchers (and the excellent Guava
library) to help us express succinctly what we want our code to do.
Java code
You know the drill! Let's roll up our sleeves and dish out some code. The following
looks pretty good:
public List<List<Integer>> groupThem(final List<Integer> list) {
final List<Integer> inputList =
Colletions.unmodifiableList(list);
final List<List<Integer>> result = Lists.newArrayList();
int i = 0;
while( i < inputlist.size()){
i = pickUpNextGroup(i, inputList, result); // i must progress
}
return result;
}
private int pickUpNextGroup(final int start, final
List<Integer> inputList,
final List<List<Integer>> result) {
Validate.isTrue(!inputList.isEmpty(),
"Input list should have at least one element");
Validate.isTrue(start <= inputList.size(), "Invalid start
index");
final List<Integer> group = Lists.newArrayList();
int currElem = inputList.get(start );
[ 11 ]
This code has a lot of subtlety. We use the Apache commons 3 validation API for
asserting the invariants. Download this book's source code to check out the unit test.
We are using the excellent Guava library to work with lists. Note the ease of this
library, with which we can create and populate the list in one line. Refer to https://
code.google.com/p/guava-libraries/wiki/CollectionUtilitiesExplained
Going scalaish
Remember the sermon on being abstract? Our Java code is dealing with a lot of
higher level and lower level detailsall at the same time... We really don't want to
do that; we wish to selectively ignore the details... We really don't want to deal with
all the corner cases of iterationthe Java code is worried stiff about looking at two
consecutive elements and the corner cases.
[ 12 ]
Chapter 1
What if this was somehow handled for us so we can focus on the business at hand?
Let's take a look at the following example:
def groupNumbers(list: List[Int]) = {
def groupThem(lst: List[Int], acc: List[Int]): List[List[Int]] = lst
match {
case Nil => acc.reverse :: Nil
case x :: xs =>
acc match {
case Nil => groupThem(xs, x :: acc)
case y :: ys if (x - y == 1) =>
case _ =>
acc.reverse :: groupThem(xs, x :: List())
}
}
groupThem(list, List())
}
groupNumbers(x)
Thinking recursively...
This version looks a lot betterdoesn't it? We use features such as nested functions
and recursion and get stuff such as immutability for free... A list in Scala is
immutable.In Java, we need to work a bit more to make things immutable. In Scala,
it is the other way around. You need to go a little further to bring in mutability... We
also use something called persistent data structures that have nothing to do with
databases here. We will look at it in detail in a while...
However, a bit better version followsmeaning a tail-recursive version. Take a look
at the following example of it:
def groupNumbers(list: List[Int])(f: (Int, Int) => Boolean) :
List[List[Int]] = {
@tailrec
def groupThem(lst: List[Int], result: List[List[Int]], acc:
List[Int]): List[List[Int]] = lst match {
case Nil => acc.reverse :: result
case x :: xs =>
acc match {
case Nil => groupThem(xs, result, x :: acc)
[ 13 ]
The @tailrec function does some good to our code. This is an annotation that we
put on a method. We use it to make sure that the method will be compiled with tail
call optimization (TCO). TCO converts the recursive form into a loop.
If the Scala compiler cannot apply TCO, it flags an error. Recursion and TCO are
covered in detail in Chapter 3, Recursion and Chasing Your Own Tail.
Now, having set the stagelet's look at the reusability feature...
A predicate is a function that returns a Boolean value. It should take two numbers
and tell whether the predicate is true or false?
Now there could be multiple predicates. For example, we might want to find
elements that differ by 2 or 3 numbers. We can define an interface as follows:
public interface Predicate {
boolean apply(int nextElem, int currElem);
}
public class MyPredicate implements Predicate {
public boolean apply(int nextElem, int currElem) {
return nextElem - currElem == 1;
}
}
[ 14 ]
Chapter 1
We would want to reuse the algorithm correctly and pick up two consecutive
elements. This algorithm would handle all corner cases as in the previous
cases.
Here is how grouping would look; I am just showing the changed code:
public List<List<Integer>> groupThem(List<Integer> list, Predicate
myPredicate) {
...
while (int i = 0; i < inputList.size();) {
i = pickUpNextGroup(i, inputList, myPredicate, result); //
i must
// progress
...
}
}
private int pickUpNextGroup(int start, List<Integer> inputList,
Predicate myPredicate,
List<List<Integer>> result) {
...
final int nextElem = inputList.get(next); // next is in range
// if (nextElem - currElem == 1) { // grouping condition
if (myPredicate.apply(nextElem, currElem)) { // grouping condition
group.add(nextElem);
...
}
}
It is some work to define and use the predicate. Scala makes expressing this
variability pretty easy, as we can use functions. Just pass on your criteria as a
function and you are good to go:
def groupNumbers(list: List[Int])(f: (Int, Int) => Boolean) :
// 1
= {
case _ =>
acc.reverse :: groupThem(xs, x :: List())
}
}
groupThem(list, List())
}
val p = groupNumbers(x) _ // 3
p((x, y) => y - x == 1)
// 4
p((x, y) => y - x == 2)
// 5
[ 16 ]
Chapter 1
At 3, we hold on to everything else except the function. And at 4 and 5, we just pass
different functions. It would be more correct to say that we use function literals. lo
and behold we get the answers right.
This code also shows some currying and partially applied functions in action We
cover both of these features in Chapter 6, Currying Favors with Your code.
[ 17 ]
Now the following snippet should work after installing the scalaz library:
scala> import scalaz.syntax.std.list._
import scalaz.syntax.std.list._
scala> List(1, 2, 3, 4, 6, 8, 9) groupWhen ((x,y) => y - x == 1)
res3: List[scalaz.NonEmptyList[Int]] = List(NonEmptyList(1, 2, 3, 4),
NonEmptyList(6), NonEmptyList(8, 9))
So we are just not writing any of the supporting codejust stating our criteria
for grouping.
You can read more about Scalaz at http://eed3si9n.com/learning-scalaz/.
Scala idioms
When do we know a language? In English, when we say a penny for your thoughts,
we are using an idiom. We can express ourselves more succinctly and natively using
these. Beating around the bush is another one. If we pick up enough of these and
hurl them around at times, this will makes us fluent.
It is almost the same with programming languages. You see a construct time and
again and make use of notable features of a specific programming language. Here is
a sample of idioms from a few prominent languages.
For example, here is an idiomatic Scala way to sum up two lists of numbers:
scala> val d1 = List(1, 2, 3, 4, 5)
d1: List[Int] = List(1, 2, 3, 4, 5)
[ 18 ]
Chapter 1
We could do this in a roundabout way; however, note that your Scala colleagues
would quickly comprehend what is happening. Try the following command:
scala> (1 to 100).map( _ * 2 ).filter(x => x % 3 == 0 && x % 4 == 0 && x
% 5 == 0)
res2: scala.collection.immutable.IndexedSeq[Int] = Vector(60, 120, 180)
For numbers from 1 to 100, we multiply each number by 2. We select those numbers
that are divisible by 3, 4, and 5.
Here we are chaining method calls together. Each method returns a new value. The
input is left unmodified. The intermediate values are threaded from call to call.
We could also write the preceding command as follows
scala> val l1 = 1 to 100
// output elided
scala> val l2 = l1.map(_ * 2)
// output elided
scala> val l3 = l2.filter(x => x % 3 == 0 && x % 4 == 0 && x % 5 == 0)
l3: scala.collection.immutable.IndexedSeq[Int] = Vector(60, 120, 180)
People relate easily to idiomatic code. When we learn and use various Scala idioms,
we will write code in the Scala way.
[ 19 ]
We expect the call a.method1 to complete before the a.method2 call starts. On the
other hand, consider a real life situation.
Let's say you go to a restaurant, sit at a table, and order food. The waiter scribbles
down the order on a piece of paper. This piece of paper then goes to the kitchen,
someone cooks the food, and the food is served. It makes sense to prepare food for
someone who ordered earlier, so your order is queued.
In the preceding paragraph, the piece of paper holds the details of your order. This
is the command object. The preceding description also talks about a queue where the
someone who cooks is the invokerhe puts things in motion as per your command.
Add the undo() functionality and you have the essence of the command design
pattern. Database transactions need to undo the commands on failurethe rollback
semantics, for example.
Here is a simple first cut as example:
def command(i : Int) = println(s"---$i---")
def invokeIt(f : Int => Unit) = f(1)
invokeIt(command)
This is a bit unpalatable though. I can possibly pass any function whatsoever and
possibly wreak havoc. So, to constrain the things we can possibly pass on to the
invoker, we take a bit of help from the compiler by typing the following commands:
scala> case class Command(f: Int => Unit)
defined class Command
[ 20 ]
Chapter 1
invokeIt: (i: Int, c: Command)Unit
It is so terse.
Scala makes it a breeze by using these strategy... Type the following command to sort
an array:
scala> List(3, 7, 5, 2).sortWith(_ < _)
res0: List[Int] = List(2, 3, 5, 7)
[ 21 ]
Scala's functions are first-class objects, meaning they can be sent to a method and
returned from a method, just like a number or string. The ability to pass functions to
other functions is a very powerful concept. It is one of the major pillars of FP, as we
will soon see.
[ 22 ]
Chapter 1
Summary
Scala is expressive and rich with tools that help us to eliminate boilerplates. It allows
us to concisely express the intention of programming. Functions help us reuse the
common facilities, freeing us from coding them every time.
Pure functions are simpler to reason about, as there are lot less moving parts. We
can think of them in terms of referential transparency. Impure functions are hard
to reason with. We saw how making things immutable also helps in reducing these
moving parts.
Idioms are what make us use a language effectively. This is true for programming
languages. Scala is a feature-rich functional programming language. We got a bird's
eye view of a few Scala features, such as recursion and functions.
Design patterns are a programmer's vocabulary. Scala gives us a fresh perspective of
patterns, and we saw how the use of functions makes using design patterns so very
easy in Scala.
We implemented the solution to a problem in Java and Scala. We saw how succinct
and expressive the Scala code is compared to its Java counterpart.
We got our feet wet in the Scala land. Let's look at these features in detail and see
how Scala makes programming cool and fun again. We will start with singleton and
factories. Get, set, and go!
[ 23 ]
www.PacktPub.com
Stay Connected: