High Order Functions

FILE : HighOrderFunctionsDemo

High order function is a function which receive or/and return other functions.
In Scala methods can be converted into functions so you can also hear about "high order methods".

//method
def highOrderMethod(f:Int=>Int): Int = f(1)

//function
val highOrderFunction : (Int=>Int) => Int = function => function(1)

The first place where you can find this construction is collection API

val simpleAddFive:Int=>Int = i => i+5
List(1,2,3).map(simpleAddFive)

Return a Function

The same as we can pass a function to other function we can also return a function. This is very handy when we want to modify given function so it fits composition or if we want to inject initial data into function.

Here we have an example of function which adds provided value to fixed initial value

def createAddToN(n:Int) : Int => Int = i => i+n
val addToThree=createAddToN(3)

Or we can return function with the same types as input but with some additional functionality - this is a functional implementation of a decorator pattern

title("Functional Decorator")
    def decorateWithLogging(original:Int=>Int) : Int=>Int = i => {
      println(s" [LOGGING] : received parameter : $i")
      original(i) //calling original
}

val decrement : Int=>Int = i => i-1

val decorated=decorateWithLogging(decrement)

Dependency injection

With high order function you can implement basic dependency injection which can create operations on injected context.

For example we can inject Dao and then return function which will operate on this DAO.

def injectDao(dao:UserDao) : Int=> Option[User] =  id => dao.findById(id)

val dao= new UserDao

val daoFunction: (Int) => Option[User] =injectDao(dao)

section("daoFunction(1) : "+daoFunction(1))

Cyrrying

The idea behind currying is that instead returning flat value you will return a function which can be applied later.

So in the following example

$$f(x) -> g(y) -> z$$

you can first apply 'x' _and later apply '_y'

val g = f(x=...)
val z = g(y= ..)

or in short
val z = f(x=..)(y=..)

In scala example this will look like this

 def calculateGross(tax:Double)(net:BigDecimal): BigDecimal = net + (net * tax)
 val calculateGrossForSpecificTax:BigDecimal -> BigDecimal = calculateGross(0.23)

Or if you want to use function syntax

val calculateGross : Double => BigDecimal =>  BigDecimal = tax => net =>  net + (net * tax)

This way you can easily separate application of particular arguments which give you more design options on how to configure/parameterize given logic.

Exercises

FILE : HighOrderFunctionsExercises

Exercise1

In this exercise you have to implement two methods which are returning function.

def createAddPrefix(prefix: String): String => String = ???
def createComparatorToConstant(constant: Int): Int => Boolean = ???

both are in the first tests when you have to complete them. Use test assertion as a spec.

Exercise2

This time you have to implement your own andThen implementation. Remember not to use standard 'andThen' from Function class.

Exercise3

This time you have inject proper logging level and as a result create an usable logger :

object Exercise3{
  sealed trait LogLevel
  case object DEBUG extends LogLevel
  case object INFO extends LogLevel
  case object ERROR extends LogLevel

  def createLogger(level:LogLevel) : String => String = ???
}

Exercise4

This is most advanced exercise in this section. You will have to implement your own curry/ uncurry methods

object Exercise4 {
  def curry(f:(Int,Int)=>Int) : Int => Int => Int = ???
  def unCurry(f:Int => Int=>Int) : (Int,Int) => Int = ???

  def curryGeneric[A,B,C](f:(A,B)=>C) : A => B => C = ???
  def unCurryGeneric[A,B,C](f:A => B => C) : (A , B) => C = ???
}

So for example when you have function with two parameters as a result of curry you should receive one param function which will return function with second parameter

val addInts: (Int,Int) => Int = (i1,i2) => i1+i2
val intsCurried: Int => Int => Int = Exercise4.curry(addInts)

After fiunishing version just for int try to implement generic version

val repeatString : (String,Long) => String = (s,n) => (1L to n).map(_ => s).mkString
val repeatStringCurried: String => Long => String = Exercise4.curryGeneric(repeatString)

results matching ""

    No results matching ""