# 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)
```