Авторизоваться
Аким Солянкин 20.05.2021 Опубликована

3 ключевых принципа функционального программирования для инженерии данных

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

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

В этом посте я представляю:

  1. Что такое функциональное программирование
  2. Ключевые принципы функционального программирования и их влияние на проектирование конвейера данных

Что такое функциональное программирование

Функциональное программирование - это декларативный стиль программирования, в котором особое внимание уделяется написанию программного обеспечения с использованием только:

  1. Чистые функции
  2. Неизменяемые значения.

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

Концепция чистых функций является ядром функционального программирования и имеет важное значение для того, как принципы функционального дизайна могут быть использованы при масштабном проектировании приложений данных. А пока вот упрощенное определение «чистой функции» [1, 2]:

  1. Выход чистой функции зависит только от ее входных параметров и ее внутреннего алгоритма (т. е. «черного ящика», в который вводятся входные параметры).
  2. Чистая функция не имеет побочных эффектов; она не имеет взаимодействия чтения / записи с внешним миром.
  3. Как следствие двух вышеупомянутых операторов, если чистая функция вызывается с входным параметром x бесконечное число раз, она всегда будет возвращать один и тот же результат y - независимо от любого изменения состояния внутреннего или внешнего процесса.

Декларативное против императивного программирования

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

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

Хотя процедурное и объектно-ориентированное программирование позволяет выражать программы в процедурах, которые легче понять программисту без необходимости вникания в детали, полная программа по-прежнему является обязательной, поскольку порядок выполнения операторов (также известный как поток управления ) влияет на то, как изменяется состояние программы.

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

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

Ключевые принципы функционального программирования

Ключевые принципы функционального программирования:

  1. Чистые функции и отсутствие побочных эффектов
  2. Неизменность
  3. Референтная прозрачность

Чистые функции и отсутствие побочных эффектов

Когда мы смотрим на математическую функцию y = f (x), мы ожидаем, что функция f не будет делать ничего, кроме вычисления результата y с учетом входных данных x.

Другими словами, чистая функция не оказывает заметного влияния на выполнение программы, кроме возврата результата (что является ее основным эффектом).

Функция с побочными эффектами изменяет состояние вне области локальной функции. Примеры побочных эффектов включают [1, 4]:

  • изменение переменной или структуры данных на месте
  • изменение глобального состояния
  • выполнение любых операций ввода-вывода (чтение или запись в файл / базу данных, печать на консоль или чтение пользовательского ввода и т. д.)
  • выброс исключения с ошибкой

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

Чтобы испечь гавайскую пиццу с тонким тестом, нам понадобится тесто для пиццы и начинка, при температуре духовки 160 градусов Цельсия в течение 10 минут. Входы для функции запекания в духовке:

  • тип корка для пиццы (тонкое)
  • список начинок (сыр, помидор, ветчина, ананасовый чатни)
  • температура духовки (в градусах Цельсия)
  • время выпечки (в минутах)

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

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

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

1. детерминированной и идемпотентной

2. без побочных эффектов

На самом деле, мы можем иногда открывать дверцу духовки, чтобы проверить процесс выпечки. (Операция ввода / вывода)

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

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

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

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

Неизменность

Неизменяемость означает, что после того, как значение присвоено переменной, состояние переменной не может быть изменено.

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

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

Ключевым результатом неизменности является простота написания параллельных / одновременных программ в функциональном программировании.

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

Поскольку Python в основном разработан как объектно-ориентированный язык программирования, его императивные шаблоны проектирования приводят к осложнениям в управлении параллельным доступом к общим переменным, которые изменяются по умолчанию — отсюда необходимость глобальной блокировки интерпретатора (GIL) для блокировки потоков и предотвращения гонки данных.

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

Ссылочная прозрачность

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

В книге Пола Кьюзано и Рунара Бьярнасона « Функциональное программирование на Scala » ссылочная прозрачность формально определяется следующим образом [1]:

Выражение e является ссылочно прозрачным, если для всех программ p все вхождения e в p могут быть заменены результатом вычисления e без влияния на значение p.

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

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

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

Функция является идемпотентной, если ее можно применять несколько раз без изменения результата за пределами ее первоначального применения. Идемпотентная функция удовлетворяет следующему условию: f (f (x)) = f (x). Примерами идемпотентных функций являются функция идентичности, функция абсолютного значения и постоянные функции.

Достаточные условия ссылочной прозрачности можно проиллюстрировать следующей аналогией:

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

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

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

При проектировании воспроизводимых конвейеров данных в масштабе наличие ссылочной прозрачности в коде дает следующие преимущества:

  1. Идемпотентность функций гарантирует программисту, что функции преобразования данных в программе воспроизводимы за пределами исходного приложения.
  2. Это позволяет программисту выражать код в более кратких и удобочитаемых функциях и значениях, улучшая читаемость при кодировании.
  3. Это позволяет программисту сосредоточиться на отладке в пределах области действия функции, не беспокоясь об изменениях состояния вне области действия функции, улучшая ремонтопригодность основных преобразований в конвейере данных.

Что дальше: функциональное программирование для проектирования конвейеров данных

В этом посте мы узнали о:

  1. Функциональное программирование и чем оно отличается от императивного программирования
  2. Понятие чистых функций
  3. Ключевые принципы функционального программирования и их влияние на проектирование конвейера данных
Коментарии
Авторизоваться что-бы оставить комментарий
Присоединяйся в тусовку
Наш сайт использует файлы cookie для вашего максимального удобства. Пользуясь сайтом, вы даете свое согласие с условиями пользования cookie