Алексей Пирогов про ФП Пт 22 марта 2019

Алексей Пирогов провел вебинар на тему «Функциональное программирование как парадигма: цели, способы, применимость». Вебинар был транслирован на YouTube-канале Hexlet, автоматизированной площадке обучающей программированию.

Алексей Пирогов — хаскеллист, в прошлом питонист. Недавно он пополнил ряды преподавателей Hexlet и теперь готовит курс по Python на этой площадке. Он сделал мини-курс на Code-Basics. Имеет канал в Telegram и на Youtube, а также он является соорганизатором «Клуба Программистов в Москве». В слак-канале Hexlet Алексея можно найти под ником @astynax.

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

Что такое ФП

Функциональное программирование — это отделение чистого кода от кода с побочными эффектами.

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

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

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

Ключевые свойства

  • Функции высшего порядка
  • Иммутабельные структуры данных

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

Имутабельные структуры данных — данные, которые нельзя изменить на уровне языка. Оперируя над такими данными, мы не можем изменить саму структуру, а можем вернуть новую версию этой структуры. Иммутабельные структуры данных реализованы везде по-разному. В современных компиляторах они оптимизированы и работают быстро.

Чистый код просто:

  • Анализировать
  • Самое важное свойство ФП — способность рассуждать о функциях. Человеку легче оперировать с тем, что находится вблизи него. В ФП легче держать весь контекст функции в голове. Не надо думать о глобальных переменных, которые могут быть изменены после вызова функции. Посмотрев тело функции, можно легко понять все, что с ней происходит.

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

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

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

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

Удобно писать

  • Компиляторы, парсеры, генераторы чего-либо
  • Сложную логику, ложащуюся на DSL (Например Sigma)
  • Высококонкурентный код (Например код на Erlang)
  • GUI (Например React)

Как научиться

Чтобы научиться настоящему ФП, нужно изучить Haskell.

Почему Haskell?

Haskell – чистый функциональный язык, который позволяет изучить ФП как подход и писать идиоматический для ФП код. Есть другой вариант для изучения, например Standart ML, но этот язык мертвый. Haskell же хорош тем, что у него есть живое сообщество. Помимо этого Haskell — статически-типизированный язык, который заодно научит интересному подходу к типам, отличающегося от типов в других языках.

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

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

Теория категорий

Есть такой миф, что в Haskell не стоит лезть, если не знаешь теорию категорий. Это неправда. Я из теории категорий примерно знаю содержание первых двух страниц типичной книги и это мне это не мешает писать production-код на Haskell.

Теория категорий — это приятная штука для развития мозгов в первую очередь, она даже не про программирование. Она может помочь стать лучше как программист, а может и не помочь.

Что еще из языков

  • Изучите Clojure (ибо LISP и макросы)
  • Потому что современный живой лисп в функциональной парадигме.

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

    После Erlang можно пописать на Elixir. Он похож на Ruby с рельсами.

Про парадигмы

Существует мнение, что стоит изучать по одной новой парадигме в год. Изучать их нужно на максимально ярких для этих парадигм языках. Хотим изучить ФП — берем Haskell. Хотим изучить ООП — берем SmallTalk. Хотим изучить Мета-программирование — берем лиспы. Хотим логическое программирование — берем Prolog.

Какая парадигма лучше всего? Никакая, все парадигмы клевые.

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

Для каких задач не подходит ФП

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

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

О типизации

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

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

Также круто пописать на динамически-типизированных языках. Например Clojure.

Про работу

Все люди, которых я знаю, если хотят писать на Haskell, то пишут на Haskell за деньги. Люди, которые ищут отговорки, продолжают писать на чем-то ином. Я в этом убежден. Работа есть.

Если технология не мертва, значит она кому-то нужна.

Сейчас, когда можно работать удаленно, найти работу по Haskell не составляет сложности. Если ты вчерашний студент, можно очень круто устроиться. Раз в полгода приходят вакансии от Tesla идти в магистратуру в Штаты, чтобы потом писать на Haskell ПО, которое анализирует метрики приходящие с автомобиля.

Ответы на вопросы

Lisp — функциональный язык

Это неправда. Лисп — не функциональный язык. Код на лиспе содержит присваивание и измененных структур данных. Это процедурный код.

SICP — это не книжка по функциональному программированию. Она использует некоторые моменты, свойственные ФП. Это книга про информатику в общем. В самой книге присутствуют термин “Процедура”, а не “Функция”.

Clojure — это Lisp-подобный язык, но функциональный. Clojure вдохновлялась Lisp’ом на этапе зарождения концепций, но в отличии от него это functional-first язык. Все структуры данных иммутабельные. Разделения на чистые и грязные функции там нет, тем не менее в нем отсутствует практика злоупотреблять побочными эффектами.

Применяются ли ФП-элементы в Python

Применяются, но в Python принято их избегать исторически. Использование только map, filter, reduce не делает код функциональным. Это синтаксический сахар для пайплайнов, которые их обрабатывают.

ООП vs ФП

Нельзя сказать что ООП очень сильно противоречит ФП. Они про разное. Они не противоположны, они ортогональны. Что важнее для создания продукта парадигма языка или библиотеки?

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

Если вдруг у кого-то работа связана только со склеиванием библиотек и не приходится писать свой код, то не важно какая парадигма будет использована. Но такое происходит редко.

Материалы для изучения

Задачи

Другие ссылки

Заключение

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

Тем не менее спасибо Алексею и команде Hexlet за вебинар. Человека для создания курса по питону наверное лучше не найти, с нетерпением жду нового материала от него. Было бы круто дополнить раздел с книгами.