 ## Lambdas and higher order functions

Chapter 5 of the Kotlin in Action book has a fantastic deep dive into lambdas.

Chapter 8 of the Kotlin in Action book has a great deep dive into higher order functions.

The following is an overly complex example of a calculator function that has 2 plugins (to add and subtract), built using higher order functions.

``````run {
val plusPlugin: (Int, Int) -> Int = { x, y -> x + y }
val minusPlugin: (Int, Int) -> Int = { x, y -> x - y }
val calc: (Int, Int, (Int, Int) -> Int) -> Int =
{ x, y, plugin ->
val value = plugin(x, y)
println(value)
value
}
calc(1, 2, plusPlugin)
calc(10, 5, minusPlugin)
"complex-calculator-example"
}
``````

## Extension Functions and Lambdas

Extension Function Expressions combine:

1. Extension functions - Functions added to a type w/out modifying the original
2. Function expressions - Undeclared function bodies used as an expression (data)
3. High order functions - A function that takes a function or returns a function

Here’s an unsophisticated example of using the 3 things above. This is a simple extension function that allows a List of Strings to be filtered Run the code in the Kotlin playground.

``````fun main() {
val data = listOf("monkey", "donkey", "banana", "apple")
println( data.filter{ it.startsWith("b") } )
}

fun <T> List<T>.filter(allow: (T) -> Boolean): List<T>{
val newList = ArrayList<T>()
for( item in this ){
}
return newList
}
``````

### Example 1

Here’s the sophisticated version of this leveraging Extension Function Expressions! Run the code in the Kotlin playground.

``````fun main() {
val data = listOf("monkey", "donkey", "banana", "apple")
println( data.filter{ startsWith("m") } )
}

fun <T> List<T>.filter(allow: T.() -> Boolean): List<T>{
val newList = ArrayList<T>()
for( item in this ){
}
return newList
}
``````

Notes:

1. `allow: T.() -> Boolean` is used instead of `allow: (T) -> Boolean` (from the unsophisticated example). The `T.` means that the function expression is an extension function of `T` itself!

2. This means that `allow()` is an extension function of `T`!

3. Since `allow()` is an extension function of `T`, `this` is passed to it, which in this case is a `String`.

4. Given the change above, the `if (item.allow()) { newList.add(item) }` statement is used instead of `if (allow(item)) { newList.add(item) }` (from the unsophisticated example).

### Example 2

The following example shows a toast, and allows the creation of a simple DSL syntax for creating the toast. And there’s no way to forget calling `show()` once it’s created!

``````inline fun toast(context: Context,
text: String = "",
duration: Int = Toast.LENGTH_SHORT,
functor: Toast.() -> Unit) {
val toast: Toast = Toast.makeText(context, text, duration)
toast.functor()
toast.show()
}
``````

Example of using the function above (with simple DSL syntax for making the toast).

``````toast(fragment.getParentActivity()) {
setText(R.string.message_cant_make_autocomplete_request_if_location_is_null)
duration = Toast.LENGTH_LONG
}
``````

Note that the `toast()` function has 1 required parameter (`context`) and the other parameters are optional (and have default values). This means that you can call this function w/ just the 1st argument. In the DSL syntax, the middle 2 arguments aren’t passed, and only the 1st argument (`fragment.getParentActivity()`) and the last argument (the lambda expression) is passed.

### Example 3

Very similar to Example 2, but this is for showing a Snackbar. Again, there’s no way to forget calling `show()` after the Snackbar has been created.

``````inline fun snack(view: View,
text: String = "",
duration: Int = Snackbar.LENGTH_SHORT,
functor: Snackbar.() -> Unit) {
val snackbar: Snackbar = Snackbar.make(view, text, duration)
snackbar.functor()
snackbar.show()
}
``````

How the function above might be used.

``````snack(fragmentContainer) {
setText(R.string.message_making_api_call_getCurrentPlace)
duration = Snackbar.LENGTH_SHORT
}
``````

### Example 4

This is an example of a non local return, Kotlin in Action, Ch 8 for an extension function w/ lambdas.

``````public inline fun <T : AutoCloseable?, R> T.use(block: (T) -> R): R {
var exception: Throwable? = null
try {
return block(this)
} catch (e: Throwable) {
exception = e
throw e
} finally {
try { this.close() }
}
}
``````

Example of using this is shown below.

``````fun readFirstLineFromFile(path: String): String {
}
}
``````

Notes:

• When the return executes in the lambda, it returns from the function in which the lambda was called from (not just the lambda block itself).

• The return from the outer function is possible only if the function that takes the lambda as an argument is inlined.

• More information on when to inline extension functions in Kotlin in Action, Ch 8. Basically its best to include a function extension that accepts lambdas (IntelliJ IDEA has hints that help with this).

You can write a local return from a lambda expression as well. A local return in a lambda is similar to a break expression in a for loop. It stops the execution of the lambda and continues execution of the code from which the lambda was invoked. To distinguish a local return from a non-local one, you use labels. You can label a lambda expression from which you want to return, and then refer to this label after the return keyword.

``````data class Person(val name: String, val age: Int)
val people = listOf(Person("Alice", 29), Person("Bob", 31))
fun lookForAlice(people: List<Person>) {
people.forEach label@{
if (it.name == "Alice") return@label
}
println("Alice might be somewhere")
}
``````