Что такое higher-order functions?

Функции высшего порядка (higher-order functions) — это функции, которые либо принимают другие функции в качестве аргументов, либо возвращают функции как результат, либо и то и другое. Это один из краеугольных камней функционального программирования и активно используется в языке Kotlin, который поддерживает функции как объекты первого класса.

Основные характеристики

  1. Функции — это объекты
    В Kotlin функции могут быть переданы в другую функцию, сохранены в переменной или возвращены из функции.

  2. Функции высшего порядка могут использоваться для управления потоком выполнения
    Например, можно передавать действия, которые нужно выполнить позже, или оборачивать повторяющееся поведение.

Простой пример

fun operate(x: Int, y: Int, operation: (Int, Int) -> Int): Int {
return operation(x, y)
}
fun sum(a: Int, b: Int): Int = a + b
fun main() {
val result = operate(5, 3, ::sum)
println(result) // 8
}

Здесь:

  • operate — функция высшего порядка.

  • operation — функция, переданная как параметр.

Лямбды как аргументы

Функции высшего порядка часто принимают лямбда-выражения как аргументы.

fun repeatAction(times: Int, action: (Int) -> Unit) {
for (i in 0 until times) {
action(i)
}
}
fun main() {
repeatAction(3) { index ->
println("Iteration $index")
}
}

Возврат функции как результата

fun multiplier(factor: Int): (Int) -> Int {
return { number -> number \* factor }
}
fun main() {
val triple = multiplier(3)
println(triple(5)) // 15
}

Здесь multiplier возвращает функцию, которая умножает число на заданный множитель.

Синтаксис функции высшего порядка

fun <T, R> transform(value: T, operation: (T) -> R): R {
return operation(value)
}
  • operation — функция, которая принимает T и возвращает R.

  • Типы параметров и возвращаемого значения могут быть обобщены.

Применение в стандартной библиотеке Kotlin

Множество стандартных функций Kotlin — это функции высшего порядка:

  • filter, map, reduce, fold, forEach, takeWhile, onEach, let, apply, run, also, with и т. д.

Примеры:

val list = listOf(1, 2, 3, 4, 5)
val even = list.filter { it % 2 == 0 }
val doubled = list.map { it \* 2 }
val sum = list.reduce { acc, i -> acc + i }

Все эти методы принимают функции в виде лямбд.

Примеры создания DSL

Функции высшего порядка активно применяются в построении DSL (domain-specific language) в Kotlin:

fun html(block: HtmlBuilder.() -> Unit): HtmlBuilder {
val builder = HtmlBuilder()
builder.block()
return builder
}
class HtmlBuilder {
fun body(block: () -> Unit) {
println("<body>")
block()
println("</body>")
}
}

Такой подход позволяет создавать выразительные структуры вроде:

html {
body {
println("Hello")
}
}

Тип функции как значение

В Kotlin функцию можно присвоить переменной и передать как параметр:

val add: (Int, Int) -> Int = { a, b -> a + b }
fun calculate(a: Int, b: Int, operation: (Int, Int) -> Int): Int {
return operation(a, b)
}
val result = calculate(4, 2, add) // 6

Использование с inline

Когда функции высшего порядка вызываются часто (особенно в циклах), можно использовать inline, чтобы избежать создания объектов лямбд и повысить производительность.

inline fun runWithLog(action: () -> Unit) {
println("Start")
action()
println("End")
}

Частичное применение и каррирование

Функции высшего порядка можно использовать для создания частично применённых функций:

fun makeGreeting(greeting: String): (String) -> String {
return { name -> "$greeting, $name!" }
}
val sayHello = makeGreeting("Hello")
println(sayHello("Alice")) // Hello, Alice!

Пример сложной функции высшего порядка

fun <T> executeIfNotNull(value: T?, action: (T) -> Unit) {
if (value != null) action(value)
}
fun main() {
val name: String? = "Kotlin"
executeIfNotNull(name) { println("Hello, $it!") }
}

Это шаблон безопасного вызова действия только если значение не null.

Преимущества

  • Переиспользуемость: Можно писать обобщённые, повторно используемые функции.

  • Читаемость: Код становится выразительным и кратким.

  • Гибкость: Можно динамически передавать поведение в виде функции.

Функции высшего порядка — это мощный инструмент, который позволяет писать декларативный, выразительный и модульный код в стиле функционального программирования. Они особенно ценны в Android-разработке, асинхронном программировании, реализации событий, реактивных потоков, UI DSL и многом другом.