Функциональное программирование должно стать вашим приоритетом №1 в 2015 году

в 12:48, , рубрики: erlang, haskell, ocaml, будущее здесь, Программирование, Софт, функциональное программирование

— ООП не сможет больше спасать нас от «Облачных монстров».

Примечание переводчика: Есть два понятия — параллельность (выполнение одновременно, независимо) и конкурентность (выполнение по шагам, поочерёдно, но одновременно несколько задач) и как всегда, мне пришлось поломать голову подобрая правильные термины.

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

Возможно вы уже слышали такое выражение, вроде: “Clojure”, “Scala”, “Erlang” или даже “Java теперь имеет лямбды”. И вы имеете хоть и отдалённое представление о «Функциональном программировании». Если вы участник какого-либа программисткого сообщества, тогда эта тема могла уже вами обсуждаться.

Если вы поищите в Google по словосочетанию «Функциональное программирование», вы не увидите что-то нового. Второй язык из созданных ранее уже охватывает эту тему, он был создан в 50-ых и называется Lisp. Тогда, какого чёрта, эта тема стала популярна только сейчас? Всего то 60 лет спустя?

В начале, компьютеры были очень медленными

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


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

Но компьютеры сильно улучшились

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

Функциональное программирование 50.5

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

Вы можете понимать термин «Функциональное программирование» слишком буквально, как программирование с использованием функций и это недалеко от истины. Вы будете создавать функции в терминах других функций и писать функции (Вы помните f ∘ g из школьной программы? Сейчас вам это пригодится). Это всё.

Список (не полный) особенностей ФП:

  1. Функции первого класса (First-Class Functions)
  2. Функции высшего порядка (High-Order Functions)
  3. Чистые функции (Pure Functions)
  4. Замыкания (Closures)
  5. Неизменяемое состояние (Immutable State)

Сейчас вы не должны переживать об этих странных терминах, просто поймите что они означают.

Функции первого класса значит, что вы сможете сохранять функции в переменные. Я уверен вы делали что-то похожее, как в примере на JavaScript:

var add = function(a, b){
  return a + b
}

Вы только что создали анонимную функцию, которая получает a и b и возвращает a + b в переменную add.

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

И снова на JavaScript:

document.querySelector('#button')
  .addEventListener('click', function(){
    alert('yay, i got clicked')
  })

или

var add = function(a){
  return function(b){
    return a + b
  }
}
 
var add2 = add(2)
add2(3) // => 5

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

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

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

var add = function(a){
  return function(b){
    return a + b
  }
}
 
var add2 = add(2)
add2(3) // => 5

Снова взгляните на второй пример из параграфа Функции высшего порядка, переменная a была замкнута и доступна только в возвращаемой функции. На самом деле, замыкания не особенность ФП парадигмы, скорее оптимизация.

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

let x = 5;;
x = 6;;

print_int x;;  (* prints 5 *)

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

Объектно-ориентированное программирование не может больше вас защищать

Это многообещающее время, когда мы сможем иметь распределённые и многопоточные приложения, наконец-то наступило. К сожалению, наша существующая (или наиболее используемая) модель не готова к многопоточности (concurrency) и параллельности (parallelism), хотя она и решает текущие задачи, но и добавляет большие проблемы.

Чтобы улучшить приложения, мы нуждаемся в простом и надёжном способе добиться желаемого. Вы помните выше упомянутые особенности ФП? Чистые функции и неизменяемое состояние? Правильно, вы можете выполнять функции тысячи раз, на разных ядрах или машинах и результат будет всегда одинаковым. И так, мы можем один и тот-же код запускать и на одном ядре и на тысячах. Жизнь опять становится безоблачной.

«Но почему я не могу продолжать использовать ООП?»

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

Пишу это не для того, чтобы агитировать вас переходить на ФП с тех парадигм, которые вы сейчас используете (хотя некоторые люди скажут я должен), но вы однозначно должны усвоить: Java и C++11 уже имеют лямбда исчисления. Могу сказать, что почти все современные и поддерживаемые языки собираются реализовать ФП особенности или уже сделали это.

Стоить отметить, что мы не должны прекращать использовать изменяемое состояние. Мы должны использовать ввод/вывод (IO) и т.д., чтобы наши программы были полезны. Основная идея ФП является: используй изменяемое состояние, только тогда, когда это действительно необходимо.

«Я не работаю с облаками, мне действительно нужно изучать ФП?»

Да.

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

«Я пытался. Это слишком сложно и код трудночитаемый»

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

Начинать изучать ФП — это почти тоже самое, как снова начать писать программы с нуля (вне зависимости какой язык вы начали изучать, это будет однозначно как начать с самого начала).

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

Просмотрите программу написанную на Haskell и JavaScript (в императивном стиле):

guess :: Int -> [Char]
guess 7 = "Much 7 very wow."
guess x = "Ooops, try again."

-- strongly inspired by http://learnyouahaskell.com

function guess(x){
  if(x == 7){
    return "Much 7 very wow."
  }
  else {
    return "Oops, try again."
  }
}

Это очень простая программа. Она выводит поздравительное сообщение, когда пользователь угадал и ввёл цифру 7 или выведет сообщение об ошибке во всех других случаях. Возможно это выглядит шифровкой, как Haskell может делать всю работу всего в две строки кода (первую строку вы можете игнорировать, это просто «объявление типа»). Но это станет достаточно простым, однажды, поняв возможности сопоставления с образцом (Pattern Matching) (которые реализованы не только в ФП языках, но были их особенностью).

Что делает Haskell:

Если принимаемый аргумент у функции guess равен цифре 7, то она вернёт «Much 7 very wow.» или вернёт «Oooops, try again.» в других случаях.

И это тоже самое, что делает код на JavaScript, но Haskell сопоставляет с «образцом», объявленным программистом в коде.

Данный подход может показаться не очень полезным в данном случае, если вы можете использовать if/else. Но это станет действительно полезным, когда вы сами начнёте писать более сложные структуры данных.

plus1 :: [Int] -> [Int]
plus1 []      = []
plus1 (x:xs)  = x + 1 : plus1 xs 

-- plus1 [0,1,2,3]
-- > [1,2,3,4]

В программе выше, *plus1* функция, которая принимает список целых чисел и прибавляет по 1 к каждому элементу списка. Функция сопоставляет, когда список пустой [] (возвращает другой пустой список, раз в нём нет элементов) обходит не пустой список и определяет образец сопоставления: x как первый элемент списка, xs как оставшийся список. Потом просто считает сумму и объединяет через рекурсивный вызов.

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

Итак, давайте начнём

Вышло много материалов по Функциональному программированию, но эти ссылки вы не должны пропустить однозначно:

  1. Principles of Functional Programming in Scala: курс будет полезен для тех, кто знает Java и хочет попробовать Функциональное программирование не спрыгивая с JVM. Курс охватывает базовые концепции.
  2. Paradigms of Computer Programming — Fundamentals: курс будет полезен для тех, кто хочет узнать как писать программы на функциональном языке. На курсе используется обучающий язык Qz. В курсе много упражнений, вы сможете использовать функциональный язык и попробовать создать свою структуру данных. Полагаю, курс предоставляет строительные блоки для «здания» с названием «Функциональное программирование», он поможет вам с другими языками в будущем.

К сожалению, курсы будут доступны только в конце года. Но вы можете следить за контентом и видео, они доступны на Youtube.

С другой стороны, если вы предпочитаете текстовые материалы, я непременно рекомендую некоторые из них:

  1. Structure and Interpretation of Computer Programs
  2. How to Design Programs
  3. Concepts, Techniques, and Models of Computer Programming

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

Кроме того, Крис Аллен (Chris Allen) написал отличную статью по изучению Функционального программирования. Она называется Functional Education и имеет исчерпывающий список материалов для изучения Функционального программирования используя Haskell, а так же расскажет о сильных и слабых сторонах этого подхода. Следуя рекомендованным Крисом ссылкам, вы сможете изучить начальные принципы и более сложные темы (я уверен вы слышали о монадах) Функционального программирования и возможно поймёте как писать приложения используя их. (Спасибо тебе Крис, за ссылки на материалы.)

Удачи в вашем Функциональном Новом Году! ☺

Автор: greg_fat

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js