Scala Tips - separating common from varying

It is a best practice to separating common from varying in the Scala. Common parts are implemented as functions and the varying parts will be arguments:

def createSeqOnCriteria(start:Int, end:Int,                              // createSeqOnCriteria: createSeqOnCriteria[](val start: Int,val end: Int,val criteria: Int => Boolean) => scala.collection.immutable.IndexedSeq[Int]                    
                        criteria: Int => Boolean) = {                    //                                                                                                                                                                       
  for (i <- start to end if criteria(i)) yield i                         //                                                                                                                                                                       
}                                                                        //                                                                                                                                                                       
                                                                         //                                                                                                                                                                       
// you can pass any functional literal Int => Boolean                    //                                                                                                                                                                       
createSeqOnCriteria(1,10, { a:Int => a > 5})                             // res0: scala.collection.immutable.IndexedSeq[Int] = Vector(6, 7, 8, 9, 10)                                                                                             
createSeqOnCriteria(1,10, { a:Int => a > 5 && a <8})                     // res1: scala.collection.immutable.IndexedSeq[Int] = Vector(6, 7)                                                                                                       
                                                                         //                                                                                                                                                                       
// using placeholders with only bound variables                          //                                                                                                                                                                       
def createSeqOnCriteria1(start:Int, end:Int, data:Int,                   // createSeqOnCriteria1: createSeqOnCriteria1[](val start: Int,val end: Int,val data: Int,val criteria: (Int, Int) => Int) => scala.collection.immutable.IndexedSeq[Int] 
                        criteria: (Int,Int) => Int) = {                  //                                                                                                                                                                       
  for (i <- start to end) yield criteria(i,data)                         //                                                                                                                                                                       
}                                                                        //                                                                                                                                                                       
                                                                         //                                                                                                                                                                       
createSeqOnCriteria1(1,10, 5, _.max(_))                                  // res2: scala.collection.immutable.IndexedSeq[Int] = Vector(5, 5, 5, 5, 5, 6, 7, 8, 9, 10)                                                                              
createSeqOnCriteria1(1,10, 5, _.min(_))                                  // res3: scala.collection.immutable.IndexedSeq[Int] = Vector(1, 2, 3, 4, 5, 5, 5, 5, 5, 5)                                                                               
createSeqOnCriteria1(1,10, 5, _ * _)                                     // res4: scala.collection.immutable.IndexedSeq[Int] = Vector(5, 10, 15, 20, 25, 30, 35, 40, 45, 50)                                                                      
                                                                         //                                                                                                                                                                       
// simplified version                                                    //                                                                                                                                                                       
def createSeqOnCriteria2(start:Int, end:Int, criteria: (Int) => Int) = { // createSeqOnCriteria2: createSeqOnCriteria2[](val start: Int,val end: Int,val criteria: Int => Int) => scala.collection.immutable.IndexedSeq[Int]                      
  for (i <- start to end) yield criteria(i)                              //                                                                                                                                                                       
}                                                                        //                                                                                                                                                                       
                                                                         //                                                                                                                                                                       
//simplified version with the free variable                              //                                                                                                                                                                       
var n = 4                                                                // n: Int = 4                                                                                                                                                            
createSeqOnCriteria2(1,10, _ * n)                                        // res5: scala.collection.immutable.IndexedSeq[Int] = Vector(4, 8, 12, 16, 20, 24, 28, 32, 36, 40)                                                                       

Above examples shows few examples how to you placeholder (_) as well 1. Going bit further, high order functions take functions as argument parameters.

Process of currying

Here the concept of currying in the Scala 2. In the following code, first illustrate the functional literal to form a partial function. Second is the the use of curried function directly. The third approach is partial function with the place holder.

// Illustrate the curring process               //                                                         
def add (a:Int): Int => Int = (b:Int) => a + b  // add: add[](val a: Int) => Int => Int                    
val partA = add(1)                              // partA: Int => Int = <function1>                         
val partB = partA(2)                            // partB: Int = 3                                          
                                                //                                                         
// The curried function is as follows           //                                                         
def curriedAdd(a:Int)(b:Int) = a +b             // curriedAdd: curriedAdd[](val a: Int)(val b: Int) => Int 
curriedAdd(1)(2)                                // res0: Int = 3                                           
                                                //                                                         
// As partial function                          //                                                         
val curriedPartA = curriedAdd(1)_               // curriedPartA: Int => Int = <function1>                  
curriedPartA(2)                                 // res1: Int = 3                                           

As explained in the Programming in Scala section 9.4 2, if a control pattern repeated in multiple parts of the code, better consider to create new control structure for that.

Reference


  1. Blog post Scala Tips: Functions and Closures 

  2. Programming in Scala, Third Edition, by Bill Venners, Lex Spoon, Martin Odersky, Publisher: Artima Press, Release Date: April 2016, ISBN: 9780981531687. 

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