Scala Tips: Functions and Closures
In the Scala functions are first class. For example function literal can be possible to assign to object as follows:
val f = (x:Int ) => x + x //f: Int => Int = <function1>
f(2) // res0: Int = 4
The different between function literal and function value is that literal exists in source code and the value exists in the runtime. Compiler do the target typing if you don't specify the type for the x in the above source. To more precise, you can use the _
as a placeholder instead of x
bound variable in the above code because x is a parameter to the function. For simplicity :
//collection supports
val l1 = for (i <- 1 to 10) yield i //l1: scala.collection.immutable.IndexedSeq[Int] = Vector(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
l1.filter(_ > 3 ) //res1: scala.collection.immutable.IndexedSeq[Int] = Vector(4, 5, 6, 7, 8, 9, 10)
In the above source, when _
used that is called partially applied function because don't supply all of the arguments needed by the expression in the function.
def sum(a:Int, b:Int, c:Int) = (a + b) * c // sum: sum[](val a: Int,val b: Int,val c: Int) => Int
val a = sum _ // leave off all the parameters // a: (Int, Int, Int) => Int = <function3>
val multiply = a(1 , 2, _:Int) // leave off one parameter // multiply: Int => Int = <function1>
multiply(3) // res0: Int = 9
In the above source, sum is a closed term because there is only bound variables need to satisfy the functional literal.
But if there is free variable which is not supplied via bound variable parameters, that is called open term.
//
var noOfAccess : Int = 0 // noOfAccess: Int = 0
val c = scala.collection.mutable.Map("SL"->"Sri Lanka", "AU"-> "Australia") // c: scala.collection.mutable.Map[String,String] = Map(SL -> Sri Lanka, AU -> Australia)
def findCountry(countries: scala.collection.mutable.Map[String, String]) ={ // findCountry: findCountry[](val countries: scala.collection.mutable.Map[String,String]) => String => String
(code:String) => {noOfAccess += 1; countries(code)} //
} //
//
val closure = findCountry(c) // closure: String => String = <function1>
closure("AU") // res0: String = Australia
//
//
//if change the free variable //
c += "UK"-> "United Kingdom" // res1: scala.collection.mutable.Map[String,String] = Map(SL -> Sri Lanka, AU -> Australia, UK -> United Kingdom)
closure("UK") // res2: String = United Kingdom
//
print(noOfAccess) // 2res3: Unit = ()
//
val closure1 = findCountry(c) // closure1: String => String = <function1>
closure("UK") // res4: String = United Kingdom
print(noOfAccess) // 3res5: Unit = ()
//
In the above source, closure find the open term which need to be closed. The varaible countries
is a free variable (is not a parameter from the functional literal but) within the scope where functional literal is defined. The variable closure
has reference to the countries
. If you change the free variable still the closure adjust for that. Important to note that free variable changed outside of the closure. Also closure can change the free variable as well. As shown in the above example, same noOfAccess
visible to the clousre
and closure1
.
Partial Functions
Here the example for partial function
// partial functions //
case class Human(name:String, gender:Boolean) // defined class Human
val female:Human = Human("Theresa", true) // female: Human = Human(Theresa,true)
val male: Human = Human("Mike", false) // male: Human = Human(Mike,false)
//
val pregnant: PartialFunction[Human,String] = { // pregnant: PartialFunction[Human,String] = <function1>
case Human(_, true) => "Lady" //
} //
//
val notPregnant: PartialFunction[Human,String] = { // notPregnant: PartialFunction[Human,String] = <function1>
case Human(_, false) => "Man" //
} //
//
val person = pregnant orElse notPregnant // person: PartialFunction[Human,String] = <function1>
//
pregnant.isDefinedAt(female) // res6: Boolean = true
pregnant.isDefinedAt(male) // res7: Boolean = false
//
person(female) // res8: String = Lady
person(male) // res9: String = Man
//
pregnant // res10: PartialFunction[Human,String] = <function1>
It is important to note that isDefined
function which test the applicability of the class to the partial function.
Monomorphic and Polymorphic functions
Monomorphic functions working on the only single data type. Polymorphic functions 1 can have any type as shown in the following code.
object PolymorphicFunctions {
def monorphicFind( intArray: Array[Int], key: Int): Int = {
def go(n:Int): Int = {
if (n >= intArray.length ) -1
else if (intArray(n) == key) n
else go(n+1)
}
go(0)
} //> monorphicFind: (intArray: Array[Int], key: Int)Int
val nums = Array(10,3,4,5,7) //> nums : Array[Int] = Array(10, 3, 4, 5, 7)
monorphicFind(nums, 4) //> res0: Int = 2
def polymorphicFind[A]( aArray: Array[A], f: A => Boolean ): Int = {
def go(n:Int): Int = {
if (n >= aArray.length ) -1
else if (f(aArray(n))) n
else go(n+1)
}
go(0)
} //> polymorphicFind: [A](aArray: Array[A], f: A => Boolean)Int
// for Int
polymorphicFind(nums,(x:Int) => x == 5) //> res1: Int = 3
// for String
val s = Array("A", "B", "C", "D", "E") //> s : Array[String] = Array(A, B, C, D, E)
polymorphicFind(s,(x:String) => x == "D") //> res2: Int = 3
}
As shown in the above code you can use the polymorphicFind
to any type of array.
Variadic Functions
This is the code, I directly tested from the source1.
sealed trait TList[+A]
case object Nil extends TList[Nothing]
case class Cons[+A](head: A, tail:TList[A]) extends TList[A]
def sum(ints: TList[Int]): Int = ints match {
case Nil => 0
case Cons(x, xs) => x + sum(xs)
}
def toList[A](as:A*):TList[A] = {
if (as.isEmpty) Nil
else Cons(as.head, toList(as.tail: _*))
}
sum(Cons(1,Cons(2,Cons(3,Nil))))
sum(toList(1,2,3,4,5,6)) //use of variadic function
In the above code A*
is the way to define the sequence of elements. And values are passed as _*
to the toList
function. Here the visualisation of the sum()
function: C is always a black box, we know only about the head. We added only the headers, until get the Nil. Here Nil will stop the process in case returning 0.

The variadic function is toList
which accept the unlimited number of arguments of type A
. It is common of apply
method of variadic function according to the source1.
This is the example where variadic apply can be used:
sealed trait TList[+A]
case object Nil extends TList[Nothing]
case class Cons[+A](head: A, tail:TList[A]) extends TList[A]
//companion object
object TList {
def apply[A](as:A*):TList[A] = {
if (as.isEmpty) Nil
else Cons(as.head, apply(as.tail: _*))
}
}
val ints = TList(1,2,3)
val strs = TList("A", "B", "C")
// to print the above lists
def display[A](es:TList[A], f: A => String): String = es match {
case Nil => "Nil"
case Cons(x, xs) => x +" -> "+ display(xs,f)
}
display(ints, x => "val: "+x) // res1: String = 1 -> 2 -> 3 -> Nil
display(strs, x => "val: "+x) // res2: String = A -> B -> C -> Nil
Reference
-
Functional Programming in Scala Video EditionPaul Chiusano, Runar Bjarnason ↩
Comments
Post a Comment
commented your blog