12. High Order Functions (call by name, by value) - RobertMakyla/scalaWiki GitHub Wiki
KEY POINTS: Functions are 'first-class citizens' in Scala, just like numbers. You can create anonymous functions, usually to give them to other functions. A function argument specifies behavior that should be executed later. Many collection methods take function parameters, applying a function to the values of the collection. There are syntax shortcuts that allow you to express function parameters in a way that is short and easy to read. You can create functions that operate on blocks of code and look much like the built-in control statements.
Functions as Values
val myf = (x:Double) => -x // setting function.
myf(2) // example usage --> -2
Giving Function to another function as parameter
Array(3.14, 1.42, 2.0).map(myf) // map() accepts functions --> Array(-3.14, -1.42, -2.0)
Anonymous Functions
(x: Int) => 3 * x // anonymous function
val triple = (x: Int) => 3 * x // anonymous function stored in variable
val triple: Int => Int = x => 3 * x // anonymous function stored in variable
val triple: Int => Int = _ * 3 // anonymous function stored in variable
val triple = x => 3 * x // ERROR
val triple = _ * 3 // ERROR
Array(2, 3).map( (x: Int) => 3 * x ) // --> Array(6, 9)
Array(2, 3).map( x => 3 * x ) // the same
Array(2, 3).map( _ * x ) // the same
Array(2, 3).map( triple ) // the same
def triple(x: Double) = 3 * x // THE SAME but here I provide name to function
// it's not anonymous
Higher-Order Functions
-
Functions that take Function as Parameters
def useFunctionWithOneQuarterArgument(f: (Double) => Double) = f(0.25)
The 'useFunctionWithOneQuarterArgument' takes function as parameter and puts 0.25 argument to this function, and returns what this function returned
useFunctionWithOneQuarterArgument( (x:Double) => 400 * x ) // --> 100.0
The type of useFunctionWithOneQuarterArgument is ((Double) => Double) => Double
-
Functions that return Function
def multiBy(factor: Double) = (x:Double) => factor * x def multiBy(factor: Double): Double => Double = x => factor * x // OK cause returned type is specified def multiBy(factor: Double): Double => Double = _ * factor // OK cause returned type is specified multiBy(10) // returns function multiBy(10)(2) // 20 val tmp = multiBy(10) // saves to tmp function tmp(2) // 20
The type of multiBy is (Double) => (Double => Double)
Parameters
def useFunctionWithOneQuarterArgument(f: (Double) => Double) = f(0.25)
useFunctionWithOneQuarterArgument( (x:Double) => 400 * x ) // 100
The function knows that parameter must be of type (Double) => Double, so I don't need to declare type of 'x' :
useFunctionWithOneQuarterArgument( (x) => 400 * x ) // 100
If it's only one parameter on the left side, I can skip the () :
useFunctionWithOneQuarterArgument( x => 400 * x ) // 100
If the single parameter occurs only once on the right side, I can write:
useFunctionWithOneQuarterArgument( 400 * _ ) // 100
e.g. 1 to 3 map { 2 * _ } // (2, 4, 6)
val fun = 3 * _ // Error: Can't establish return type
val fun = (x: Int) => 3 * x // OK
val fun = 3 * (_: Int) // OK
Useful Higher-Order Functions
(1 to 10).map("*" * _).foreach(println _) // foreach(f: (Int) => U)
// foreach applies function to each argument
(1 to 9).filter(_ % 2 == 0) // filter( f: (Int => Boolean) )
(1 to 9).filter( x => x % 2 == 0) // the same
(1 to 3).reduceLeft(_ + _ ) // reduceLeft takes 2 args function and applies it from left:
// ((1 + 2) + 3) = 6
(1 to 3).reduceRight(_ + _ ) // (1 + (2 + 3)) = 6
(1 to 9).reduceLeft(_ * _) // reduceLeft[B >: Int](f: (B,Int) => B)
// reduceLeft takes 2 args function and applies it from left:
// (...((1 * 2) * 3) * ... * 9)
"ccc a bb eeeee dddd".split(" ").sortWith(_.length < _.length)
// splits with spaces and sorts: Array(a, bb, ccc, dddd, eeeee)
Closures
A function can be in any scope: package/class/function/method
def getMultiFunctionByFactor(factor : Double) = (x : Double) => factor * x
val tripleFunction = getMultiFunctionByFactor(3)
val halfFunction = getMultiFunctionByFactor(0.5)
Each of functions has its own factor, set through argument. Such function is called a CLOSURE.
Single Abstract Methods (SAM) Conversions
In Scala we can pass functions as arguments. In Java we pass implementations of classes with SAM.
Currying
Turning a function that takes two arguments into a function that takes one argument and returns curried function which takes also one argument and returns value.
def mul(x: Int, y: Int) = x * y // takes 2 args
def mulOneAtATime(x: Int) = (y: Int) => x*y // takes 1 arg, returns curried function
// curried function also takes 1 arg
mulOneAtATime(6)(6) // 36
def mulOneAtATime(x: Int)(y: Int) = x * y // shortcut for curried function
def mulBySix(x:Int) = mulOneAtATime(6)(x)
Call by-value vs. by-name
def predicate: Boolean = {
println("calling predicate") // side effect
100 > 1 // return value
}
def callByValue(x: Boolean) = { // `x` -evaluated before entering callByValue
println("x1 = " + x)
println("x2 = " + x)
}
def callByName(x: => Boolean) = {
println("x1 = " + x) // 'x' function is evaluated twice inside
println("x2 = " + x) // the callByName()
}
x
function is evaluated twice insidecallByName()
, because println()
takes parameters by-value. In other case, x
would not be evaluated but just passed deeper.
scala> callByValue(predicate)
calling predicate
x1 = true
x2 = true
scala> callByName(predicate)
calling predicate
x1 = true
calling predicate
x2 = true
In call by-value the value is always evaluated during the call of function
In call by-name the value is evaluated inside function, once it's called by-value at any level
In call by-name the value may never be evaluated. Eg:
val assertingIsEnabled = false
def assertByName(predicate: => Boolean){
if(assertingIsEnabled){
assert(predicate)
}
}
assertByName( (1/0) > 1 ) // no error cause asserting is disabled
// predicate is never evaluated
assertByValue( (1/0) > 1 ) // ERROR : Division by zero
() => Boolean vs. => Boolean
If we had a definition def callByName(x: () => Boolean)
that's just taking function as parameter
Then we would need to call it:
callByName( () => predicate )
callByName( () => 100 > 1 )
With def callByName(x: => Boolean)
that's proper call by-name
We can call it like that and not worry that it's evaluated immediately:
callByName( 100 > 1 ) // evaluated inside the function
callByName(predicate) // i.e. not immediately
Control Abstractions
Providing 1 function as parameter
def runInThread(block: () => Unit) { // parameter is a function'()=>Unit'
new Thread {
override def run() { block() } // block is evaluated in new thread
}.start() // not before
}
E.g:
runInThread {
() => { println("Hi");
Thread.sleep(1000);
println("Bye") }
}
Changing it to Call-by-name
-
If (block: () => Unit) is changed to call by-name (block: => Unit)
-
Then () => { println("Hi")} can be replaced with println("Hi")
-
But also block() must be replaced with block because in 'call by-name' block it treated as a value ( evaluated at first usage of call by-value)
def runInThread(block: => Unit) { // call by-name
new Thread {
override def run() { block } // block is evaluated in new thread
}.start() // not before
}
runInThread { println("Hi"); Thread.sleep(1000); println("Bye") }
Two parameters called by-name
def keepDoingWhen(condition: => Boolean)(block: => Unit) {
if (condition) {
block
keepDoingWhen(condition)(block)
}
}
Usage of call by-name
var x = 10
keepDoingWhen (x > 0) { // The 'x > 0' is not valuated here
x -= 1
println(x)
}
or the original way (without call by-name) the same but ugly cause we are passing functions:
def keepDoingWhen(condition: () => Boolean)(block: () => Unit) {
if (condition()) {
block()
keepDoingWhen(condition)(block)
}
}
var x = 10
keepDoingWhen( () => x > 0) { // the same but ugly
() => x -= 1
println(x)
}