Функции как значения первого класса
В функциональном программировании функция — такое же значение, как число: её можно хранить, передавать и возвращать.
«Когда функции становятся значениями, программа превращается в конструктор, где детали — это поведения.»
Метод (через def) привязан к месту, где объявлен. А функция-значение — это самостоятельный объект, который можно положить в val. Записывается она с помощью стрелки =>.
val square = (x: Int) => x * x
val add = (a: Int, b: Int) => a + b
println(square(6)) // 36
println(add(2, 3)) // 5Слева от => — параметры, справа — тело. Тип square — это Int => Int («функция из Int в Int»). Тип add — (Int, Int) => Int.
Зачем это нужно
Раз функция — значение, её можно передать в другую функцию как аргумент. Это основа всей функциональной мощи Scala: вы описываете что делать, передавая поведение.
def applyTwice(f: Int => Int, x: Int): Int =
f(f(x))
println(applyTwice(square, 2)) // square(square(2)) = 16
println(applyTwice(_ + 1, 10)) // 12Подчёркивание _ — это краткая запись: _ + 1 означает (x) => x + 1.
Метод можно превратить в функцию
def triple(x: Int): Int = x * 3
val f = triple // метод стал функцией-значением (eta-расширение)
println(f(7)) // 21Та же идея на Python ▶
# Функции — значения первого класса и в Python
square = lambda x: x * x
add = lambda a, b: a + b
def apply_twice(f, x):
return f(f(x))
print(square(6)) # 36
print(apply_twice(square, 2)) # 16
print(apply_twice(lambda v: v + 1, 10)) # 12
# функцию можно положить в переменную и передать
fns = [square, add]
print(fns[0](5)) # 25функция-значение:
(x: Int) => x * x
\______/ \___/
параметры тело
тип: Int => Int
вход выходКак работает под капотом (JVM)
У JVM нет понятия «функция-значение» — она знает только объекты. Поэтому Scala-функция (x: Int) => x * x компилируется в объект, реализующий интерфейс вроде Function1[Int, Int] с методом apply. Когда вы пишете square(6), на самом деле вызывается square.apply(6). В современных версиях для эффективности используются invokedynamic и лямбды JVM, так что накладные расходы минимальны.
Частые ошибки
- Путать метод и функцию-значение.
def— это метод,val f = (...) => ...— функция-значение. Передавать в higher-order функции можно оба. - Злоупотреблять
_. Краткая запись удобна, но в сложных случаях явная лямбда читается лучше. - Забывать тип параметра в лямбде. Иногда Scala не может его вывести — тогда укажите явно.
Best practices
- Передавайте поведение как функции — это делает код гибким и переиспользуемым.
- Используйте
_для совсем коротких лямбд, явный синтаксис — для понятности. - Предпочитайте чистые функции без побочных эффектов.
Граница между данными и поведением стирается
Идея «функция — это значение» поначалу кажется технической деталью, но она лежит в основе всего функционального программирования. Когда функцию можно положить в переменную, передать аргументом и вернуть результатом, поведение становится таким же гибким материалом, как числа и строки. Вы перестаёте делить мир на «пассивные данные» и «активный код» — теперь и то, и другое можно складывать, передавать и комбинировать одними и теми же средствами.
Практически это открывает целый стиль программирования. Вместо того чтобы зашивать конкретное действие внутрь функции, вы принимаете действие параметром и применяете его. Так одна функция обслуживает множество сценариев. Краткая запись через подчёркивание делает простые лямбды почти невидимыми, не отвлекая от сути, а eta-расширение позволяет передавать обычные методы туда, где ждут функцию-значение. Освоив этот сдвиг, вы начнёте видеть в любой повторяющейся логике кандидата на вынос поведения наружу.
Важно держать в голове, что под капотом JVM такая функция — это объект с методом apply. Это объясняет, почему функции можно хранить в коллекциях, сравнивать по ссылке и передавать как любые другие объекты: для виртуальной машины они и есть объекты. Понимание этой механики снимает ощущение магии и помогает рассуждать о производительности, когда функций становится много.
Этот сдвиг в восприятии — момент, после которого функциональное программирование начинает раскрываться. Как только поведение становится таким же передаваемым значением, как число, открывается путь к функциям высшего порядка, замыканиям и всему богатству методов коллекций, которые вы изучите дальше. Поэтому стоит потратить время и по-настоящему прочувствовать идею «функция — это просто значение».
Итоги. Функция-значение пишется через =>, имеет тип вида A => B, и её можно хранить и передавать. Это фундамент функционального стиля. Дальше — функции высшего порядка.