Чистые функции

Еще одна концепция, которую Scala предлагает для помощи в написании функционального кода, — это возможность писать чистые функции. Чистая функция (pure function) может быть определена следующим образом:

Из этого следует:

В результате этого определения каждый раз, когда вызывается чистая функция с одним и тем же входным значением (значениями), всегда будет выдаваться один и тот же результат. Например, можно вызывать функцию double бесконечное число раз с входным значением 2, и всегда получать результат 4.

Примеры чистых функций

Учитывая это определение, методы в пакете scala.math._ являются чистыми функциями:

Эти методы String также являются чистыми функциями:

Большинство методов в классах коллекций Scala также работают как чистые функции, включая drop, filter, map и многие другие.

В Scala функции и методы почти полностью взаимозаменяемы, поэтому, хотя здесь используется общепринятый отраслевой термин "чистая функция", этот термин можно использовать как для описания функций, так и методов. Как методы могут использоваться подобно функциям описано в главе Eta расширение.

Примеры "грязных" функций

И наоборот, следующие функции "грязные" (impure), потому что они нарушают определение pure function:

"Грязные" функции часто делают одно из следующего:

В общем, следует остерегаться функций с возвращаемым типом Unit. Поскольку эти функции ничего не возвращают, логически единственная причина, по которой они когда-либо вызываются, - это достижение какого-то побочного эффекта. Как следствие, часто использование этих функций является "грязным".

Но грязные функции все же необходимы…

Конечно, приложение не очень полезно, если оно не может читать или писать во внешний мир, поэтому рекомендуется следующее:

Напишите ядро вашего приложения, используя только "чистые" функции, а затем напишите "грязную" "оболочку" вокруг этого ядра для взаимодействия с внешним миром. Как кто-то однажды сказал, это все равно, что положить слой нечистой глазури на чистый торт.

Важно отметить, что есть способы сделать "нечистое" взаимодействие с внешним миром более "чистым". Например, можно услышать об использовании IO монады для обработки ввода-вывода. Эти темы выходят за рамки данного документа, поэтому для простоты можно думать, что ФП приложения имеют ядро из "чистых" функций, которые объединены с другими функциями для взаимодействия с внешним миром.

Написание "чистых" функций

Примечание: в этом разделе для обозначения методов Scala часто используется общепринятый в отрасли термин "чистая функция".

Для написания чистых функций на Scala, достаточно писать их, используя синтаксис методов Scala (хотя также можно использовать и синтаксис функций Scala). Например, вот чистая функция, которая удваивает заданное ей входное значение:

def double(i: Int): Int = i * 2

Вот чистая функция, которая вычисляет сумму списка целых чисел с использованием рекурсии:

def sum(xs: List[Int]): Int = xs match
  case Nil => 0
  case head :: tail => head + sum(tail)

Вышеописанные функции соответствуют определению "чистых".

Ключевые моменты

Первым ключевым моментом этого раздела является определение чистой функции:

Чистая функция — это функция, которая зависит только от своих объявленных входных данных и своей реализации для получения результата. Она только вычисляет свой результат, не завися от внешнего мира и не изменяя его.

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


Ссылки: