Функциональный Kotlin. Часть 2. Каррированные функции и где они обитают

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

Введение

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

Определение

Если говорить простым языком, каррирование это преобразование функции нескольких аргументов к набору функций одного аргумента. Таким образом, функция f будет преобразована в fCurried:

fun f(a: A, b: B, c: C) { ... }

fun fCurried(a: A) = fun(b: B) = fun(c: C) { f(a,b,c) }

Тут мы используем анонимные функции, делая возвращаемым значением первой функции (B) → (C) → Unit, а для второй (C) → Unit.

Такой синтаксис кажется немного странным, верно? Для ООП-языков он и правда необычен, однако для людей использующих, например, Haskell, эта конструкция тривиальна, так как в нем каррированными являются вообще все функции.

Выглядит это примерно так:

f :: A -> B -> C -> Nothing

Что-то похожее мы увидим и на Kotlin, если определим функцию не стандартным способом, а как переменную:

val g: (A, B, C) -> Unit = { ... }

val gCurried: (A) -> (B) -> (C) -> Unit = {
  a: A -> { 
    b: B -> { 
      c: C -> g(a,b,c)
    } 
  } 
}

Применяем каррирование правильно

Раз уж я упомянул Haskell, в случае с ним сам компилятор заточен под вычисления именно с такими функциями, однако Kotlin такими плюсами не обладает. Так зачем же они нам?

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

fun foo(a: A, b: B, c: C) = { ... }

Очевидно, что прописывать все аргументы каждый раз - не очень хороший подход. Что же делать?

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

Опыт работы в объектно ориентированной парадигме подсказывает очевидное, но некачественное решение:

class Bar(val a: A, val b: B) {
  
 fun foo(c: C) { ... }
 
}

Проблема такого подхода в том, что нам будет необходимо создавать и хранить несколько экземпляров класса.

Тут-то на помощь и приходит каррирование - оно позволяет нам частично определить функцию и использовать ее позднее без создания лишних объектов, усложняющих понимание кода:

val f = foo(a1, b1)
val g = foo(a2, b2)

// ...
if (expression) f(c1) else g(c1)

// ...
if (expression) f(c2) else g(c2)

А что же "под капотом"?

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

P.S.

В этот раз статья вышла довольно короткой, но и сама тема достаточно узка. Следующая (на любую из двух тем в голосовании) будет по объему на уровне первой.

Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.
О чем написать следующую?
60% Последовательности (секвентивные алгоритмы) 3
100% Монады 5
Проголосовали 5 пользователей. Воздержавшихся нет.
Источник: https://habr.com/ru/post/649817/


Интересные статьи

Интересные статьи

14 декабря — Всемирный день компьютерных игр. В этот день особенно сильно хочется остаться дома, запустить любимый олдскульный шедевр (вроде The Elder Scrolls III: Morrowind или даже Contra на Денди) ...
Максимально переложив ответственность за онлайн своего приложения, ты сможешь сфокусироваться на других задачах, думать больше о новых фичах и новых приложениях. Ведь попробуй просто представить себе,...
ВведениеВ данной статье я бы хотел рассмотреть проблему обновления PHP в виртуальной машине BitrixVM, и действия, которые возможно применить если выполнение переезда на машину с обновленным ПО невозмо...
В этом посте приводится первая часть выступления, которое я проводила для 10 групп в Y Combinator для укрепления связей между сооснователями. Вторая часть будет чуть позже. Конфлик...
И двух лет не прошло, как новая порция моих удивительных историй про приключения около IT кристаллизовалась и ждёт своих читателей. Приключения продолжаются! Присаживайся поудобнее, включай пригл...