Scala Functional Programming Patterns

4.8 (4 reviews total)
By Atul S. Khot
  • Instant online access to over 7,500+ books and videos
  • Constantly updated with 100+ new titles each month
  • Breadth and depth in over 1,000+ technologies
  1. Grokking the Functional Way

About this book

Scala is used to construct elegant class hierarchies for maximum code reuse and extensibility and to implement their behavior using higher-order functions. Its functional programming (FP) features are a boon to help you design “easy to reason about” systems to control the growing software complexities. Knowing how and where to apply the many Scala techniques is challenging. Looking at Scala best practices in the context of what you already know helps you grasp these concepts quickly, and helps you see where and why to use them.

This book begins with the rationale behind patterns to help you understand where and why each pattern is applied. You will discover what tail recursion brings to your table and will get an understanding of how to create solutions without mutations. We then explain the concept of memorization and infinite sequences for on-demand computation. Further, the book takes you through Scala’s stackable traits and dependency injection, a popular technique to produce loosely-coupled software systems.

You will also explore how to currying favors to your code and how to simplify it by de-construction via pattern matching. We also show you how to do pipeline transformations using higher order functions such as the pipes and filters pattern. Then we guide you through the increasing importance of concurrent programming and the pitfalls of traditional code concurrency. Lastly, the book takes a paradigm shift to show you the different techniques that functional programming brings to your plate.

This book is an invaluable source to help you understand and perform functional programming and solve common programming problems using Scala’s programming patterns.

Publication date:
December 2015
Publisher
Packt
Pages
298
ISBN
9781783985845

 

Chapter 1. Grokking the Functional Way

Before we start learning Scala, let's first understand what functional programming (FP) is. You may have used a spreadsheet while working. In a spreadsheet, there is a bunch of equations and we enter values in the given cells for these equations. We get the answers through these equations. When you enter the same values again, you get the same answer and there are no fallouts.

At the core of FP is composition. Looking at a software system made up of parts, we build bigger parts by composing smaller parts. If you think about it, most complex systems are composed of parts, which in turn are composed of smaller parts. Functional languages give us the means to make this composition. One of the prominent functional languages that can be used for FP is Scala.

Scala in Italian means a staircase. If you look at the language's logo, you will see it's a staircase. The language acts as a staircase through which we can ascend and become better at programming. Scala also refers to the very many techniques that we can use to control the complexity of large-scale systems.

We will begin to learn Scala by looking at abstractions. We will see why abstractions are good and how Scala helps us to be abstract. Scala code is concise and expressive. A lot of the concise expression is due to functions. Functions are an important pillar of functional programming. In this chapter, we will look at pure and impure functions. Reducing the number of moving parts to be used is an effective technique to control programming complexity. Immutability is another pillar of FP that helps us here. To see all this in effect, we will take a problem example and implement the solution in Java. Then, we will solve the problem using Scala. Looking at a few Scala one-liners will help us to get started with READ/EVALUATE/PRINT LOOP (REPL). We will use Scala REPL extensively throughout the entire book.

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 =>
     |   y._1 -> y._2.size
     | }
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 beauty—the 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...

Note

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).

You can type in expressions to have them evaluated and type help for more information. Let's try the following simple one line commands:

scala> 1 + 1
res2: Int = 2
scala> "hello".length
res4: Int = 5

For me, Scala brought the thrill back to programming... You can do a great deal by writing a few lines of code—less 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.

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.

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:

Figure 1.1: Pure functions

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 world—this means that it affects its surroundings. In this case, the variable p. Here is the diagrammatic representation for the preceding code:

Figure 1.2 :An impure function

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
             …

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 simple—a 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> name.firstName = "Mrs. Bertie"
<console>:13: error: reassignment to val
       name.firstName = "Albert"

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!

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

If we rewrite the f method as follows, the meaning won't change:

scala> def f(x: Int, y: Int, p: Int, q: Int)= f1(x, y) + f1(p, q)
f: (x: Int, y: Int, p: Int, q: Int)Int

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("-"))
1-2-9
scala> println(x.mkString("-"))
1-2-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

This substitution gave us different results. The += method of ListBuffer is not a pure function as there is a side effect that occurred. The value of the lb variable at 1 and 2 is not the same.

 

The problem – grouping continuous integers


A while back, I wanted a simple and elegant way to solve the following problem:

Given: A sorted list of numbers

When: We group these numbers

Then: Each number in a group is higher by one than its predecessor. So, let's be good and write a few tests first:

 @Test
 public void testFourGroup() {
  List<Integer> list = Lists.newArrayList(1, 2, 3, 4, 5, 9, 11, 20, 21, 22);
  List<List<Integer>> groups = groupThem.groupThem(list);
  assertThat(groups.size(), equalTo(4));
  assertThat(groups.get(0), contains(1, 2, 3, 4, 5));
  assertThat(groups.get(1), contains(9));
  assertThat(groups.get(2), contains(11));
  assertThat(groups.get(3), contains(20, 21, 22));
 }
 @Test
 public void testNoGroup() {
  List<Integer> emptyList = Lists.newArrayList();
  List<List<Integer>> groups = groupThem.groupThem(emptyList,
    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 );
  group.add(currElem); // We will have at least one element in the group

  int next = start + 1; // next index may be out of range

  while (next < inputList.size()) {
   final int nextElem = inputList.get(next); // next is in range
   if (nextElem - currElem == 1) { // grouping condition
    group.add(nextElem);
    currElem = nextElem; // setup for next iteration
   } else {
    break; // this group is done
   }
   ++next;
  }
  result.add(group); // add the group to result list
  Validate.isTrue(next > start); // make sure we keep moving
  return next; // next index to start iterating from
                // could be past the last valid index
 }

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 for more details.

We are careful to first make the input list an unmodifiable list—to protect ourselves from stepping on our toes... We also need to carefully arrange the iteration to make sure that it eventually terminates and we do not accidentally access a non-existent list index (try to say it without gulping).

Going scalaish

Remember the sermon on being abstract? Our Java code is dealing with a lot of higher level and lower level details—all 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 iteration—the Java code is worried stiff about looking at two consecutive elements and the corner cases.

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 better—doesn'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 follows—meaning 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)
        case y :: ys if (x - y == 1) =>
          
            groupThem(xs, result, x :: acc)
        
      case _ => 
            groupThem(xs, acc.reverse :: result, x :: List())
      }
  }
  val r = groupThem(list, List(), List())
  r.reverse
}

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 stage—let's look at the reusability feature...

Reusability – the commonality/variability analysis

Instead of a fixed grouping criteria, could we make the Java version more dynamic? Instead of the expressing the condition directly in the code, we could wrap up the condition as a predicate:

if (nextElem - currElem == 1) { // grouping condition	

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;
    }
}

Why would we do this? We would do this for two reasons:

  • 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.

  • The actual grouping criteria in our case is nextElem – currElem == 1, but we can reuse the previous code to regroup the numbers using another criteria.

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  = {
  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 (f(y, x)) =>
              // 2
            groupThem(xs, x :: acc)
          
    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

In 1, we use the function - (f: (Int, Int) => Boolean).

Note

The f: (Int, Int) => Boolean syntax means that f is a function parameter. The function takes two Int params and returns Boolean.

Here is a quick REPL session to understand this better:

scala> val x : (Int, Int) => Boolean = (x: Int, y: Int) => x > y
x: (Int, Int) => Boolean = <function2>
scala> x(3, 4)
res0: Boolean = false
scala> x(3, 2)
res1: Boolean = true
scala> :t x
(Int, Int) => Boolean

We are using the t feature of the REPL to look at the type of x.

This is a function—a bit of code we can pass around—that corresponds to the previous Java predicate snippet. It is a function that takes two integers and returns a Boolean.

(A function is in fact a class that mixes in a trait, function2 in our case).

At 2, we use it. Now comes the fun part.

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.

The one-liner shockers

Have you heard this phrase anytime: it is just a one-liner! You see all the work done in a single line of code, it seems almost magical. The Unix shell thrives on these one-liners. For example:

       seq 1 100 | paste -s -d '*' | bc

Phew! This single command line generates a sequence of numbers, 1 to 100, generates a multiplication expression from these, and feeds the same to bca calculator that does the actual multiplication.

Scala has a legion of these. Here is one applied to our problem by using scalaz library.

Note

To try the following snippet, you need to install the scalaz library. It is not part of the Scala standard library. Here are a few simple instructions to install it.

Create a directory of your choice and switch to it. Next, download and install the Simple Build Tool (sbt) from http://www.scala-sbt.org/download.html.

/Users/Atulkhot/TryScalaz> sbt
[info] Set current project to tryscalaz (in build file:/Users/Atulkhot/TryScalaz/)
> set scalaVersion := "2.11.7"
… // output elided
> set libraryDependencies += "org.scalaz" %% "scalaz-core" % "7.1.0"
… // output elided
> console
…

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))

Using functions the solution is just a one-liner:

scala> List(1, 3, 4, 6, 8, 9) groupWhen ((x,y) => y - x == 2)
res4: List[scalaz.NonEmptyList[Int]] = List(NonEmptyList(1, 3), NonEmptyList(4, 6, 8), NonEmptyList(9))

So we are just not writing any of the supporting code—just 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)

scala> val d2 = List(11, 22, 33, 44, 55)
d2: List[Int] = List(11, 22, 33, 44, 55)

scala> (d1, d2).zipped map (_ + _)
res0: List[Int] = List(12, 24, 36, 48, 60)

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)

However, the fluent API style is more idiomatic.

Note

You can learn more about fluent interfaces at http://www.martinfowler.com/bliki/FluentInterface.html.

People relate easily to idiomatic code. When we learn and use various Scala idioms, we will write code in the Scala way.

 

Patterns and those aha! moments


Patterns have more to do with a software design such as how to compose things together, the kind of design template to be used, and so on. They are a lot more about interactions between classes and objects. Patterns are design recipes, and they illustrate a solution to a recurring design problem. However, idioms are largely specific to a language and patterns are more general and at a higher level. Patterns are also mostly language independent.

You can (and usually do) implement the same design patterns in the language you are working with.

The command design pattern

The command design pattern encapsulates an object. It allows you to invoke a method on the object at a later point. For example, we are used to the following code line:

Given a Java object:

          a.someMethod(arg1, arg2);
          a.method1(arg1, arg2);
          a.method2(arg3);

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 invoker—he 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 failure—the 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)

Note

The def method gets converted to a function. This is called an ETA expansion. We will soon be looking at the details.

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

scala> def invokeIt(i: Int, c: Command) = c.f(i)
invokeIt: (i: Int, c: Command)Unit

scala> def cmd1 = Command(x => println(s"**${x}**"))
cmd1: Command

scala> def cmd2 = Command(x => println(s"++++${x}++++"))
cmd2: Command

scala> invokeIt(3, cmd1)
**3**

scala> invokeIt(5, cmd2)
++++5++++

It is so terse.

The strategy design pattern

Strategy helps us to define a set of algorithms, encapsulates each step, and selects one as appropriate.

Oh boy! Here it is. We surely have used the java.util.Comparator strategy interface at times that allows us to vary the compare algorithm as we see fit so we can sort arrays at will. Let's try the following example:

       Integer[] arr = new Integer[] { 1, 3, 2, 4 };
  Comparator<Integer> comparator = new Comparator<Integer>() {
   @Override
   public int compare(Integer x, Integer y) {
    return Integer.compare(y, x); // the strategy algorithm –// for reverse sorting
    }
  };
  Arrays.sort(arr, comparator);
  System.out.println(Arrays.toString(arr));

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)

Passing algorithms around

We need this ability to plug in an algorithm as needed. When we start applying a strategy, we really try to apply the Open/Closed principle (OCP). We don't touch the sort algorithm internals, that is, the sort implementation is closed for modification. However, by passing in a comparator, we can use the algorithm to sort objects of various classes that are open for extension.

This open for extension feature is realized very easily in Scala, as it allows us to pass functions around.

Here's another code snippet as an example of passing functions:

def addThem(a: Int, b: Int) = a + b // algorithm 1
def subtractThem(a: Int, b: Int) = a - b // algorithm 2
def multiplyThem(a: Int, b: Int) = a * b // algorithm 3

def execute(f: (Int, Int) => Int, x: Int, y: Int) = f(x, y)

println("Add: " + execute(addThem, 3, 4))
println("Subtract: " + execute(subtractThem, 3, 4))
println("Multiply: " + execute(multiplyThem, 3, 4))

Here, these various strategy algorithms are functions—no boilerplate. Imagine writing this in Java. This code is possible because in Scala, we can pass functions around as objects. Furthermore, we can use a method where a function is expected:

val divideThem = (x: Int, y: Int) => x / y
println("Divide: " + execute(divideThem, 11, 5))

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.

 

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!

About the Author

  • Atul S. Khot

    Atul S. Khot is a self-taught programmer and has written software programmes in C and C++. Having extensively programmed in Java and dabbled in multiple languages, these days, he is increasingly getting hooked on Scala, Clojure, and Erlang. Atul is a frequent speaker at software conferences and a past Dr. Dobb's product award judge. He was the author of Scala Functional Programming Patterns and Learning Functional Data Structures and Algorithms, published by Packt Publishing.

    Browse publications by this author

Latest Reviews

(4 reviews total)
Excellent
There are some occasional mistakes in the programs and the explanation text that follows it, but overall a good book.
Excellent

Recommended For You

Book Title
Access this book, plus 7,500 other titles for FREE
Access now