Scala Constructs: Concepts of Functional Programming
Scala Constructs: Concepts of Functional Programming
Pure functions
No side effects
Immutability
Referential transparency
Higher-order functions
Side Effects
You must have written or will be writing lot of functions in your code. Have you
realized doing any of the following actions in a function will result in side effects?
Immutability
If you cannot change something, that is immutable.
An immutable object (unchangeable object) is an object whose value or state cannot
be modified once created.
Immutability is not about restricting you to do something. It is more about dealing it
differently.
Let us see a quick example.
number = 30 gives a compilation error in Scala (val is the keyword in Scala to define
an immutable object)
Can you think of an example which is against this? Let us look at the following pseudo
code (too early to write Scala code!)
The output of the above function will not be same even if you call with the same
argument, "at different times." So the above function is not referentially transparent.
Why Scala
Scala is
Even the return type of a function can be another function! These type of functions
are called as Higher Order Functions.
Scala notation to express it: Int => Int (You can say Int gives Int !)
object Result{
def add(op1: Int, op2:Int): Int = { op1+op2 }
}
Another takeaway: Look at the second print statement in the snippet and understand
how to substitute a variable value in a string.
Calling a Higher Order Function
val myNumber = 10
val result = myHigherOrderFunction(addOne, myNumber)
println(result)
The output of the above code would be 11. Do you know how that is done?
object Result{
var multiply = (a:Int,b:Int)=>a*b// Define your function literal he
re
}
Function Literals
In Scala, functions can be expressed in function literal
For example: (x: Int) => x + 1 is a function literal
Those functions can be represented by objects, which are called function
values.
The above function can be read as, a function which takes an integer argument and
returns an integer result.
You can assign it to an object also as val f = (x: Int) => x + 1
Scala Collections
Collections let you organize large numbers of objects.
In simple cases, you can throw a few objects into a set or a list and not think
much about it.
For trickier cases, Scala provides a general library with several collection types,
such as sequences, sets and maps.
List, as the name goes, is similar to arrays. It is a collection of elements of the same
type.
As Scala is a language with lot of syntactic sugar and built-in type inference features,
programmers can write the above expression as
Map
Collections in Scala has a lot of built-in functions, and many of them are of
type higher order functions. Let us see an example.
Now, the following snippet would convert a list Integer values to a list of String,
using map function.
The function map in List type is a higher order function. It accepts a function that
operates on one element of a List at a time. The argument function maps one element
of list to another type say, U. i.e once you apply the map function on a list of
type List[T], the result you get back is a List[U].
Note: U can be same as T as well
Collection Hierarchy
Scala collections orderly differentiate immutable and mutable collections.
Collection Classes
The entire lot of collection classes are available in
the scala.collection package or one of the sub-packages
of scala.collection - generic, immutable, and mutable.
Most collection classes needed by client code exist in three variants, which
are located in packages scala.collection, scala.collection.immutable,
and scala.collection.mutable, respectively.
Each variant features distinct characteristics on mutability.
Note: By default, Scala always takes collection from the immutable
hierarchy.
For instance,
If you just write Set without any prefix, or without having imported Set from
somewhere, you will get an immutable set.
If you write Iterable, you will get an immutable iterable collection, because
these are the default bindings imported from the Scala package.
To have the mutable default versions, you have to write
explicitly collection.mutable.Set, or collection.mutable.Iterable
Filtering
There are plenty of built-in functions provided in Scala for all collections. We will look
at a few of the commonly used ones.
Here is an example of filter and _some more syntactic sugars in Scala. It is fun. Read
the commented part carefully.
The above statement also shows usage of an anonymous function. ie i => i >
5 is actually of type Int => Boolean. type of i is not explicitly given as Scala
understands that it is an Integer.
Writing it as i: Int => i > 5 is also correct.
Another simplification in terms of Syntactic sugar is to write the expression
as val g5 = lst.filter(_ > 5). _ (underscore) will be replaced by the
elements of the list by Scala compiler for you.
foreach
foreach is an example of a method that can have side effects. foreach does not
return anything. It can be used for writing the output to disk, database, printing, etc.
Do some exercises on your own. We will cover few of the complex ones in the
upcoming courses.
def ListFilter(var1: Int) {
// Put your code here
val x = List.range(0,var1)
println(x)
val g = x.filter(_ % 2 ==0)
println(g)
}
What is flatMap?
We have already tried some examples with map. flatMap is very similar to map.
However, there are some differences concerning the functions that are passed onto
them. Both map and flatMap can be applied on many other data structures and where
they are supported.
Let us use List as an example here to understand about flatMap easily.
To make it clear,
map accepts a function that returns U for a given T. Here, T => U, hope you
remember the previous example about Int => Int. T and U are generic types
here.
flatMap accepts a function that returns a List[U] for every T. ie the function
passed onto it should be of type T => List[U]. The results are finally flattened
to create a single List[U].
val s = listUntil(var1).flatMap(listUntil)
println(s)
println(s.length)
Objects
Scala is a pure object oriented language and it extends the use of classes, objects,
interfaces, etc. to programmers.
While working on building applications using Scala, you can decompose your program
into classes, objects, etc. to deal with complexities.
In other words, a class should be responsible for a reasonable amount of
functionality.
In the process of designing your classes, you can also design interfaces to those
classes that abstract away the details of their implementation.
When you write val i = 1, you create an Int object with the value 1 and
assign it to a variable. In this case, a val named i.
Similarly, when you write var s = "Happy", you create a String object with the
value "Happy" and assign it to a variable, a var named as s.
Singleton Objects
One way in which Scala is more object-oriented than Java is that classes in Scala
cannot have static members.
Instead, Scala has singleton objects. A singleton object definition looks like a class
definition, except instead of the keyword class you use the keyword object. Here’s an
example:
Don't worry about the logic in the following snippet. However you may explore what is for
comprehension in Scala.
object simpleObjectExample {
val defaultValue = 20
def doSomething(arg: Int) = { arg * 2 * 3 }
}
Classes
A class is considered as a blueprint for objects. Once you define a class, you can
create objects of it using the keyword new. For example, given the class definition:
class Home {
// class definition goes here
// define your members here (both variables and functions)
}
You can create Home objects (also known as instances of class Home) with:
new Home
A user can assign the object to a variable, for later reference and usage.
Case Class
case classes are similar to classes. You need to a put a keyword case in front
of class.
Is that all ? No.
You can build objects of them without using new keyword.
How do I create a case class.
Pattern Matching
Pattern matching is very similar to switch case structures that you would have seen
other programming languages. However, Pattern Matching in Scala has lot more to
do!
Let us start with a simple example.
Matching on Values
val times = 1
times match {
case 1 => "one"
case 2 => "two"
// _ matches everything else
case _ => "some other number"
}
You can also use the concept called guards along with pattern matching. An example
is here.
// Note the if conditions given in case
times match {
case i if i == 1 => "one"
case i if i == 2 => "two"
case _ => "some other number"
}
Notification is an abstract super class which has three concrete Notification types
implemented with case classes Email, SMS, and VoiceRecording.
def PatternMatching(var1: Int) {
var1 match {
case i: Int if i<=0 => println("Negative/Zero is input")
case i: Int if i%2==0 => println("Even number is given")
case i: Int if i%2!=0 => println("Odd number is given")
}
}
Traits
Traits are applied to share fields and interfaces between classes. These are same as
Java 8’s interfaces. Objects and classes can extend traits. However, traits cannot be
instantiated and hence have no parameters.
Defining a trait
A minimal trait is simply the keyword trait and an identifier:
ex: trait HairColor
extends is the keyword to inherit from a trait.
Traits Example
Let us look at an example
for trait.
trait BaseSoundPlayer {
def play
def close
def pause
def stop
def resume
}
class Rectangle(length : Int, breadth : Int)
{
def area()
{
println(length*breadth)
}
def perimeter()
{
val a = 2 * (length+breadth)
println(a)
}
}
object Result{
trait ArithmeticOperations {
val x :Int
val y: Int
def add: Int
def subtract: Int
def multiply: Int
def divide: Int
}
class Variables (xc: Int, yc: Int) extends ArithmeticOperations {
val x= xc
val y= yc
def add() = x + y
def subtract() = x - y
def multiply() = x * y
def divide() = x / y
}
}
Hands On -2
object Result {
/*
* Complete the 'mapHigherOrder' function below.
*
* The function accepts INTEGER_ARRAY args as parameter.
*/
def mapHigherOrder(args: Array[Int]) {
val intList :List[Int] = args.map(_.toInt).toList
// Put your code here
def isPerfectNumber(input: Int) :String = {
// Put your code here
if ( (for (x <- 2 to input/2 if input % x == 0) yield x).sum + 1 ==
input )
return "true"
else
return "false"
}
def myHigherOrderFunction(argFn: Int => String, argVal:List[Int]): List
[String] =
{ // Put your code here
return argVal.map(argFn)
}
println(myHigherOrderFunction(isPerfectNumber, intList))
}
}
HandsOn 1
object Result {
def ListHigherOrder(input: Int) {
// Put your code here
val intList = List.range(1,input+1)
def factorial(n: Int): Int = {
var f = 1
for(i <- 1 to n)
{
f = f * i;
}
return f
}
factorial(input)
def myHigherOrderFunction(fac: (Int) => Int, intList: List[Int]) =
intList.foreach((x: Int) => println(fac(x)))
myHigherOrderFunction(factorial, intList)
}
}