1. Functional Objects
&
Function and Closures
< Sandip Kumar > sandip@knoldus.com
2. Programming with Functional Objects in
Scala
Scala is the perfect mixture of Object Oriented (OO) and
Functional Programming (FP). You get the flexibility of FP
along with the familiarity of OO; along with the awesome
power of the Actor model.
3. What is the Functional Object
1- A function in Scala is a complete object. There are a
series of traits in Scala to represent functions with various
numbers of arguments: Function0, Function1, Function2, etc
2- As an instance of a class that implements one of these
traits, a function object has methods
3-One of these methods is the apply method, which contains
the code that implements the body of the function.
4-Scala has special "apply" syntax: if you write a symbol
name followed by an argument list in parentheses (or just a
pair of parentheses for an empty argument list), Scala
converts that into a call to the apply method for the named
object.
4. What is the Functional Object
5-When we create a variable whose value is a function
object and we then reference that variable followed by
parentheses, that gets converted into a call to the apply
method of the function object.
6-When we treat a method as a function, such as by
assigning it to a variable, Scala actually creates a
function object whose apply method calls the original
method, and that is the object that gets assigned to the
variable.
class test {
def m1(x:Int) = x+3
val f1 = (x:Int) => x+3
}
5. What is the Functional Object
Scala has both functions and methods and we use the
terms method and function interchangeably with a minor
difference.
# A Scala method is a part of a class which has a name, a
signature, optionally some annotations, and some bytecode
where as a function in Scala is a complete object which can
be assigned to a variable.
# In other words, a function which is defined as a member of
some object is called a method.
6. Scala is a functional language, in the sense that every function is a Value. If
functions are values, and values are objects, it follows that functions
themselves are objects. The function type S => T is equivalent to
scala.Function1[S, T] where Function1 is defined as
------------------------------------------------------------------------------------------------------
trait Function1[-S, +T] {
def apply(x: S): T
}
So functions are interpreted as objects with apply methods.For example, the
anonymous successor function (x: Int ) => x + 1 is expanded to
--------------------------------------------------------------------------------------------------------
new Function1[Int, Int] {
def apply(x: Int): Int =
x+1
7. Function Declarations:
A scala function declaration has the following form:
def functionName ([list of parameters]) : [return type]
Function Definitions:
A scala function definition has the following form:
def functionName ([list of parameters]) : [return type] = {
function body
return [expr]
}
8. Function Implementation
A function which does not return anything can
return Unit which is equivalent to void in Java and
indicates that function does not return anything.
The functions which do not return anything in
Scala, they are called procedures. Following is the
syntax
object Hello{
def printMe( ) : Unit = {
9. Calling Functions:
Scala provides a number of syntactic variations for
invoking methods. Following is the standard way to
call a method:
functionName( list of parameters )
If function is being called using an instance of the
object then we would use dot notation similar to
11. Fun With Scala Functions
scala> method1
method1
scala> method2("abc")
method2: abc
scala> method3("abcdefg")
12. Fun With Scala Functions
* When we type âdef method1() = {âŠ}â we actually
declared an instance of a special class. Iâll declare method1
again, but with the underlying object exposed:
scala> val method1 = new Function0[Unit] {
| def apply: Unit = { println("method1") }
|}
method1: java.lang.Object with () => Unit = <function>
scala> method1
res1: java.lang.Object with () => Unit = <function>
scala> method1.apply
method1
scala> method1()
13. #We instantiate an instance of trait
Function0[Unit] and implement its one
abstract method, called apply, and assign
it to a val named method1. Now you can
see method1 is actually just a plain old
Scala object. When we type in âmethod1âł
and hit enter, the interpreter just tells us
the resulting value of the statement which
is an Object with trait Function0. Hmm,
that didnât work. Next we try calling the
apply method on the object.
14. That Function0[Unit], by the way, defines a
function that takes 0 parameters and returns Unit
(which is to say nothing as in Java void (not to be
confused with Nothing)). If you want a function
that takes two parameters, an Int and a String,
and returns a List of Doubles, you would use
Function2[Int, String, List[Double]]. So class
FunctionX takes (X+1) type parameters, the first
X of which define the function parameter types,
and the last of which defines the return type.
15. scala> def method2 = { println("method2") }
method2: Unit
scala> val m2: () => Unit = method2
<console>:5: error: type mismatch;
found : Unit
required: () => Unit
val m2: () => Unit = method2
^
scala> def method2() = { println("method2") }
method2: ()Unit
16. # First we just define a function called method2. Nothing fancy. Then
we try to assign it to a val of type () => Unit. It fails. See the error
message? Found : Unit. It parses it all wrong. Scala thinks weâre
trying to call method2 and assign the result to m2. How can we set
things straight? Well, one way is to slightly change the way we define
method2. The only difference in the first and second definition is the
addition of an empty parameter list, that empty pair parentheses.
# For some reason, when we define the method in this apparently
equivalent fashion, Scala rightly interprets our intentions and allows us
to assign to m2. There is another way, though. In the third definition of
method2, weâve again removed the parentheses. But this time we
assign it successfully to val m2 by following method2 with an
underscore. The underscore just causes Scala to treat method2 as a
Function0 object, rather than attempting to invoke it.
17. Fun With Scala Functions
1-We instantiate an instance of trait
Function0[Unit] and implement its one abstract
method, called apply, and assign it to a val
named method1.
2-Now you can see method1 is actually just a
plain old Scala object. When we type in
âmethod1âł and hit enter, the interpreter just tells
us the resulting value of the statement which is
an Object with trait Function0. Hmm, that didnât
work.
18. Fun With Scala Functions
If you want a function that takes two parameters,
an Int and a String, and returns a List of Doubles,
you would use Function2[Int, String,
List[Double]]. So class FunctionX takes (X+1)
type parameters, the first X of which define the
function parameter types, and the last of which
defines the return type.
19. How can we use traits
trait Function3[-T1, -T2, -T3, +R] extends AnyRef
{
...
def apply( v1 :T1, v2 :T2, v3 :T3 ) : R
...
}
20. Fun With Scala Functions
scala> def method2 = { println("method2") }
method2: Unit
scala> val m2: () => Unit = method2
<console>:5: error: type mismatch;
found : Unit
required: () => Unit
val m2: () => Unit = method2
^
* we just define a function called method2.
21. Fun With Scala Functions
1-The only difference in the first and second
definition is the addition of an empty parameter
list, that empty pair parentheses. For some
reason, when we define the method in this
apparently equivalent fashion, Scala rightly
interprets our intentions and allows us to assign
to m2.
2- In the third definition of method2, weâve again
removed the parentheses. But this time we
assign it successfully to val m2 by following
22. Scala - Functions Call-by-Name
A call-by-name mechanism passes a code block to the callee and each time the
callee accesses the parameter, the code block is executed and the value is
calculated.
24. Scala - Function with Variable Arguments
Scala allows you to indicate that the last parameter to a
function may be repeated. This allows clients to pass
variable length argument lists to the function. Following is a
simple example to show the concept.
25. Function with Variable Arguments
object Test {
def main(args: Array[String]) {
printStrings("Hello", "Scala", "Python");
}
def printStrings( args:String* ) = {
var i : Int = 0;
for( arg <- args ){
println("Arg value[" + i + "] = " + arg );
i = i + 1;
}
26. Scala - Default Parameter Values for a Function
Scala lets you specify default values for function parameters. The
argument for such a parameter can optionally be omitted from a
function call, in which case the corresponding argument will be
filled in with the default. Following is an example of specifiying
default parameters:
27. Default Parameter Values for a Function
object Test {
def main(args: Array[String]) {
println( "Returned Value : " + addInt() );
}
def addInt( a:Int=5, b:Int=7 ) : Int = {
var sum:Int = 0
sum = a + b
return sum
}
}
28. Scala - Nested Functions
Scala allows you to define functions inside a
function and functions defined inside other
functions are called local functions. Here is
an implementation of a factorial calculator,
where we use a conventional technique of
calling a second, nested method to do the
work
29. Scala - Nested Functions
object Test {
def main(args: Array[String]) {
println( factorial(0)
println( factorial(1) )
println( factorial(2) )
println( factorial(3) )
}
def factorial(i: Int): Int = {
def fact(i: Int, accumulator: Int): Int = {
if (i <= 1)
accumulator
else
fact(i - 1, i * accumulator)
}
fact(i, 1)}}
30. Scala - Partially Applied Functions
When you invoke a function, you're said to be applying the
function to the arguments. If you pass all the expected arguments,
you have fully applied it. If you send only a few arguments, then
you get back a partially applied function. This gives you the
convenience of binding some arguments and leaving the rest to
be filled in later. Following is a simple example to show the
concept:
31. Scala - Partially Applied Functions
import java.util.Date
object Test {
def main(args: Array[String]) {
val date = new Date
log(date, "message1" )
log(date, "message2" )
log(date, "message3" )
}
32. Scala - Partially Applied Functions
*Here the log( ) method takes two parameters: date and
message. We want to invoke the method multiple times,
with the same value for date but different values for
message. We can eliminate the noise of passing the
date to each call by partially applying that argument to
the log( ) method. To do so,
# we first bind a value to the date parameter and leave
the second parameter unbound by putting an
underscore at its place. The result is a partially applied
function that we've stored in a variable.
33. Scala - Functions with Named Arguments
Named arguments allow you to pass arguments to a
function in a different order. The syntax is simply that
each argument is preceded by a parameter name and
an equals sign. Following is a simple example to show
the concept:
34. Functions with Named Arguments
object Test {
def main(args: Array[String]) {
printInt(b=5, a=7);
}
def printInt( a:Int, b:Int ) = {
println("Value of a : " + a );
println("Value of b : " + b );
}
}
35. Scala - Recursion Functions
object Test {
def main(args: Array[String]) {
for (i <- 1 to 10)
println( "Factorial of " + i + ": = " + factorial(i) )
}
def factorial(n: BigInt): BigInt = {
if (n <= 1)
1
else
n * factorial(n - 1)
36. Scala - Higher-Order Functions
Scala allows the definition of higher-order
functions. These are functions that take other
functions as parameters, or whose result is a
function. For example in the following code,
apply() function takes another function f and a
value v and applies function f to v:
38. Scala - Anonymous Functions
Scala provides a relatively lightweight syntax for
defining anonymous functions. Anonymous
functions in source code are called function
literals and at run time, function literals are
instantiated into objects called function values.
Scala supports first-class functions, which means
you can express functions in function literal
syntax, i.e., (x: Int) => x + 1, and that functions
39. Scala - Anonymous
Functions
var inc = (x:Int) => x+1
Variable inc is now a function that can be used the
usual way:
var x = inc(7)-1
It is also possible to define functions with multiple
parameters as follows:
var mul = (x: Int, y: Int) => x*y
Variable mul is now a function that can be used the
40. Scala - Currying Functions
Currying transforms a function that takes multiple
parameters into a chain of functions, each taking
a single parameter. Curried functions are defined
with multiple parameter lists, as follows:
def strcat(s1: String)(s2: String) = s1 + s2
Alternatively, you can also use the following
syntax to define a curried function:
42. Scala - Closures
A closure is a function whose return value depends on
the value of one or more variables declared outside
this function. Consider the following piece of code with
anonymous function:
val multiplier = (i:Int) => i * 10
Here the only variable used in the function body, i * 0,
is i, which is defined as a parameter to the function.
Now let us take another piece of code:
val multiplier = (i:Int) => i * factor
There are two free variables in multiplier: i and factor.
One of them, i, is a formal parameter to the function.
43. Scala - Closures
object Test {
def main(args: Array[String]) {
println( "muliplier(1) value = " + multiplier(1) )
println( "muliplier(2) value = " + multiplier(2) )
}
var factor = 3
val multiplier = (i:Int) => i * factor
}
Above function references factor and reads its current value each time. If a
44. PROBLEM
class TestClass {
| def f1(): Unit = { println("f1!!!"); func = f2 }
| def f2(): Unit = { println("f2!!!"); func = f3 }
| def f3(): Unit = { println("f3!!!"); func = f1 }
|
| var func: () => Unit = f1
|
| def test = { func() }
|}
IF tc is the object of the class than output of
scala> tc.test, scala> tc.test and scala> tc.test