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


  1. Functional Programming in Scala Video EditionPaul Chiusano, Runar Bjarnason 

Comments

Popular posts from this blog

How To: GitHub projects in Spring Tool Suite

Spring 3 Part 7: Spring with Databases

Parse the namespace based XML using Python