Scala – the Seemingly Arbitrarily Chosen Bits

Introduction

In this blog post I will do my best to explain a selection of Scala concepts. The original motivation behind the post was to improve my own understanding, since it’s a truism that the best way to learn is to teach. The concepts were chosen because they are in some way fundamental to using Scala and because I wasn’t happy with my level of understanding. Most of the concepts are Scala functions, but I’ve thrown in discussions of the underscore, and of currying. Hopefully the following will prove at least a little useful to those that have taken the Functional Programming Principles in Scala course on Coursera, and to those that have yet to take the course. (If you haven’t taken it yet, you should. It’s great)

 

map

map is perhaps the most important tool that a functional programmer has. Essentially, given a collection, we apply a function to every member and return a new collection containing the results of applying that function to the members. So for example, take

val list = List(1001, 5, 14, 1, 99)

We may, for some reason, wish to obtain a new list which contains each element of this list, incremented by one. In other words, we want to apply the following function to each element:

x => x + 1 (for each element, represented by ‘x’, we return an element which is x + 1)

With map this is easy:

list.map(x => x + 1)

This returns a new List, List(1002, 6, 15, 2, 100).

 

flatMap

flatMap is similar to map, except that it can only be given functions that return a sequence. Ultimately you will then have a sequence of sequences which can then be ‘flattened’ into one long sequence. Confused? Good.

Take the previous example. list.flatMap(x => x + 1) would throw an error because the function returns an int, and a sequence of ints (like List(1002, 6, 15, 2, 100) cannot be flattened any further. However, list.flatMap(x => List(x, x + 1)) would work because that function returns a List. We end up with a List of Lists (List(List(1001, 1002), List(5, 6), List(14, 15), List(1, 2), List(99, 100)), which can then be flattened. The result of the flattening is List(1001, 1002, 5, 6, 14, 15, 1, 2, 99, 100).

So, given our example of list.flatMap(x => List(x, x + 1)), this is what happens:

1)      First a map is performed which takes each element and returns a List of the element and the element plus 1. The result of the map function is a List of these Lists.

2)      A flattening is performed which turns the List of Lists into one single List. (There is a Scala method ‘flatten’. flatMap is equivalent to performing a map followed by a flatten).

 

filter

Another important Scala function is filter. filter iterates over a sequence and tests each element against a Boolean expression. A new sequence is returned which contains all elements for which the expression returned true. Here is an example, using the same list that we have used for previous examples:

list.filter(x => x > 10)

This returns List(1001, 14, 99).

 

Underscore

This may be a good time to take a break from explaining functions to explaining the underscore character and how it is used in Scala. It has several different uses, but the average reader will probably use it most often in anonymous functions (functions that are used without assigning them to a variable name, as with x => x > 10 in the filter example). _ essentially is used to represent ‘the generic element’ that is the subject of the function. Its usage is probably best understood by example, so I will rewrite two of the anonymous functions that we’ve seen before using the underscore:

list.map(x => x + 1) becomes list.map(_ + 1)
list.filter(x => x > 10) becomes list.filter(_ > 10)

 

The underscore can also simply be used as a placeholder in function parameters, instead of the usual ‘x’. See the groupBy examples below. If you replace the underscores with an ‘x’ you should be able to understand what the function is doing.

 

groupBy

groupBy allows us to, you guessed it, group elements of a collection together according to some characteristic. It returns a map of key value pairs where the key is the grouping characteristic and the values are the elements of the collection. Here are some examples:

 

val l = List(“Im”, “at”, “Amandas”, “wedding”, “in”, “a”, “church”, “on”, “Thomas”, “street”)
l.groupBy(_.contains(‘e’)) returns
Map(false -> List(Im, at, Amandas, in, a, church, on, Thomas), true -> List(wedding, street))
 
l.groupBy(_.length()) returns
Map(2 -> List(Im, at, in, on), 7 -> List(Amandas, wedding), 1 -> List(a), 6 -> List(church, Thomas, street))

 

 

reduceLeft & reduceRight

These two operations iterate through a sequence, applying a function to each element in turn with respect to an accumulator. That doesn’t help you? I don’t blame you. Here’s a few examples that will hopefully make things more clear:

 

val list = List(1001, 5, 14, 1, 99)
list.reduceLeft(_ + _)

 

The result of calling the above function is 1120, the sum of all the elements in the list. We have passed an operation (_ +_) to reduceLeft, which has the effect of iterating through the list from left to right, adding the right-hand operand to the result of the previous operation. So, in our example we first add 1001 to 5 to give us 1006. Moving down the list by one place, we have 14 as the right-hand operand. We add it to the result of our previous operation, 1006, to give us 1020. We move down one place so that 1 is our right-hand operand and we add it to 1020. Finally we add 99 to our previous result.

Calling list.reduceRight(_ + _) would yield the same result, as the function iterates through the list from right to left. However, list.reduceLeft(_ – _) and list.reduceRight(_ – _) WILL NOT yield the same result. reduceLeft is equivalent to 1001 – 5 – 14 – 1 – 99 = 882. reduceRight is equivalent to 1001 – (5 – (14 – (1 – 99))) = 1008.

Here are some more operations on our list using reduceLeft and reduceRight. Try them out in a scala worksheet to see if the result tallies with your understanding:

 

list.reduceLeft(_ min _)
list.reduceRight(_ * _)

 

 

foldLeft & foldRight

These two functions are broadly similar to reduceLeft and reduceRight, but they work slightly differently. Here we apply them to our list:

 

list.foldLeft(0)((x, y) => x + y)
list.foldRight(0)((x, y) => x + y)

 

These two examples both evaluate to the sum of the elements of the list. We are passing an extra parameter to the function (‘0’ in this case), which is our ‘start’ value. Whereas reduceLeft and reduceRight iterated over the list beginning with the first or last two elements, foldLeft and foldRight begin with this ‘start’ value and the first or last element. So the value you give this extra parameter affects the result that you are returned. If you wanted to find the product of the elements, you would choose ‘1’ as the start value. Do you see why?

Again, the subtraction operation yields unexpected results:

list.foldLeft(0)((x, y) => x – y) yields 0 – 1001 – 5 – 14 – 1 – 99, while list.foldRight(0)((x, y) => x – y) yields 1001 – (5 – (14 – (1 – (99 – 0). However, list.foldRight(0)((x, y) => y – x) yields 0 – 99 – 1 – 14 – 5 – 1001.

 

Currying

Currying probably isn’t all that important for a Scala beginner, but I’ve had trouble getting my head around it, conceptually, so I want to outline it here so as to make sure that I understand it. Essentially, currying means that we break down a function that takes multiple arguments into a chain of functions that each take a single argument, and return a function which also takes a single argument, until the last function is called which returns the answer. I will present a function taking multiple arguments and its curried equivalent(s). Hopefully that will illustrate the concept.

Uncurried

val curryDemo = (x:Int, y:Int, z:Int) => x + y + z
curryDemo(1, 5, 11) returns 17

 

Curried

val equivalent = curryDemo.curried
val first = equivalent(1)
val second = first(5)
second(11) returns 17

 

Alternatively, equivalent(1)(5)(11) also returns 17, as does first(5)(11).

Scala – the Seemingly Arbitrarily Chosen Bits

One thought on “Scala – the Seemingly Arbitrarily Chosen Bits

Leave a comment