Многие люди, вероятно, слышали о функциональном программировании, некоторые пытались написать свой собственный Hello World, а некоторые даже запустили свой собственный «функциональный» любимчик-проект. Но сколько людей использовали функциональные языки в производстве? Каковы преимущества и недостатки ФП? Соответствует ли парадигма функционального программирования ожиданиям разработчиков? На эти и многие другие вопросы ответил человек, обнаруживший преимущества функционального подхода после 20 лет разработки ООП.
Вагиф Абилов — разработчик норвежской компании Miles. Активно использует функциональное программирование в реальных проектах, предъявляющих высокие требования к скорости и масштабируемости.
В ООП мы заранее заставляем себя работать в узких рамках
— Вагиф, в функциональное программирование вы пришли после процедурного и объектно-ориентированного? Или это был функционал, который появился первым? Что было вашей отправной точкой? Вагиф Абилов: Когда я начал заниматься функциональным программированием, у меня уже было около 20 лет опыта в объектно-ориентированном программировании.В 90-х это был C++, а потом, с появлением .
NET, я программировал на C#.
Я типичный бэкэнд-разработчик.
Я работал с сервисами и, в основном, с проектами, где важна масштабируемость и производительность.
Можно выделить одну из основных причин, по которой я начал присматриваться к чему-то другому.
Если посмотреть, как системы такого типа пишутся с использованием ООП, то большой проблемой является так называемая общее состояние или общее состояние.
То есть если у вас многопоточная система, то у нее должен быть доступ к общим данным.
Таким образом, необходимо вручную управлять потоками, необходимо закрывать общее состояние, чтобы оно случайно не нарушилось.
Значительная часть человеческих ресурсов уходит на правильное программирование.
Вообще говоря, это не часть основной предметной области, основного функционала.
Определенным способствующим фактором является то, что было сформулировано как « Закон Амдала ", что устанавливает зависимость производительности параллельной системы от наличия ресурсов для параллельной обработки.
На конкретном примере это звучит так: "если у вас 10 процессоров, но вы распараллеливаете только 40%, то производительность увеличивается в 1,56 раза».
Таким образом, вариантов распараллеливания систем, сильно ориентированных на ограничение доступа к данным разными потоками, не так много.
В какой-то момент меня это уже не устраивало, и я стал больше смотреть на возможность решения этой проблемы средствами, которые позволили бы мне получить.
избавление от разделяемого состояния.
Преимущество многих функциональных языков в том, что по умолчанию все данные не мутируют и не могут быть изменены.
Это была первая причина, по которой я начал смотреть в сторону функционального программирования.
Около шести лет назад я получил приглашение выступить на достаточно крупной международной конференции NDC. К тому времени я уже начал работать в хобби-проектах с FP и поделился там своим опытом F#.
Это был романтический период, когда на лекции по функциональному программированию приходили в основном C#-разработчики, с удовольствием слушали, а потом спрашивали: «ну а где это применяется в реальных системахЭ» Возможно ли это вообще применить? Часто сами спикеры говорили, что не используют ФП в реальных системах, но планируют это сделать.
Я был примерно в таком же состоянии, то есть работал во всех проектах на C#, но ради интереса познакомился с F#.
Мой доклад назывался: «Игра в функциональную игру жизни Конвея», то есть реализация игры Конвея «Жизнь» методом функционального программирования.
Довольно известная игра.
Я показал, как это написать на F#, сделал, а когда начал разбираться, был удивлён.
Надо сказать, что перед этим я нашел проект реализации этой игры на C# на CodeProject. Этот проект состоял из пяти классов, пяти свойств и методов, имел более 300 строк кода и был работоспособен.
Даже если я уберу все скобки, останется около 100 строк.
Когда я писал «Жизнь» на F#, у меня в итоге получилось 14 строк исполняемого кода: семь функций в среднем по две строки (если вы думаете, что это предел — посмотри на код английский программист Фил Трелфолд, уместивший решение игры Конвея на F# в 140 символов и разместивший его в Твиттере).
Простота разработки на F# меня шокировала.
Это первое, что меня впечатлило.
Затем я начал смотреть на свой код. Я стал думать, где в этом коде написано, что решение сделано для двумерная доска ? Я обнаружил, что только одна функция, вычисляющая соседей ячейки, указывает на то, что это 2D-доска.
Если эту функцию заменить на работу с трехмерной или даже многомерной доской, то все будет работать так же.
Во многих функциональных языках, в частности в F#, существует так называемая вывод типа : когда вы прямо не указываете тип своих данных, но компилятор в зависимости от того, как вы их используете, определяет, что подставлять.
Благодаря этому вы сразу пишете обобщенный код. Если в Java или C# вам нужно специально обобщить ваши классы, то в F# это происходит по умолчанию.
Это дает очень большие преимущества, в чем я смог убедиться лично, работая над различными проектами.
Когда я в конце концов подготовил свой доклад и представил его на конференциях, я обратился к аудитории и спросил, с чего начать писать игру Конвея.
Практически все предлагали определять классы и свойства, например: нужно ввести класс «Доска», «Клетка», определить свойства «Ячейка».
То есть все привыкли, что начинать нужно с определения типов и их взаимоотношений.
Мы так привыкли.
Но на самом деле предварительное введение такого большого количества определений накладывает существенное ограничение на дальнейшую работу с ними.
Третьим важным аспектом реализации Игры Жизни на F# было то, что я не ввел там ни одного типа.
Все делалось путем указания функций.
Это дает полную свободу в том, как «играть» с исходными данными.
Я понял, что с помощью ООП мы заранее «заставляем» себя работать в рамках тех определений, которые мы ввели.
Я нашел твит, подходящий для описания этой ситуации, когда работал над презентацией своего доклада.
Какой-то англоговорящий программист написал: « Программирование на Java похоже на изучение русской литературы: нужно назвать сотню имен, прежде чем что-то начнет происходить».
.
Этот комментарий довольно хорошо определяет подход ООП.
Мы должны изначально всё хорошо описать, а уж потом начнут происходить какие-то события, и мы сможем «нанизать» на наши определения какие-то методы.
И зачастую наш дизайн уже нас ограничивает. Возвращаясь к исходному вопросу, должен сказать, что именно попытка уйти от мутирования данных стала для меня отправной точкой в мир функционального программирования.
В Java и C# слишком много церемоний.
— На ваш взгляд, стоит ли знакомиться/переходить на функционал человеку, который давно занимается объектно-ориентированным программированием? Вагиф Абилов: Вопрос полного перехода – достаточно прагматичный вопрос.
И да, безусловно, стоит познакомиться.
Если мы посмотрим на объектно-ориентированные языки, такие как Java или C#, то за последние годы они претерпели довольно много изменений.
Если я ничего не путаю, то в C# версии 3.0, когда появился LINQ, появились лямбда-выражения, это уже был заметный шаг в сторону внедрения элементов функционального программирования.
Возникает следующий аргумент: «зачем мне изучать сами функциональные языки, если мы можем многое сделать на C# с элементами функциональных языковЭ» По крайней мере один ответ на этот вопрос относится к области изменяемых структур данных, поскольку и C#, и Java Всегда останутся языки с мутациями.
Когда данные, которые вы определяете по умолчанию, изменяемы, независимо от того, какие элементы функционального программирования вы вводите, это не изменит фундаментальную суть этих языков.
В последних версиях C# вы можете поэкспериментировать с элементами FP, но, безусловно, имеет смысл попробовать поработать с настоящим функциональным языком, например Эрланг , Хаскель или Ф# .
Я бы особенно рекомендовал последний вариант, поскольку этот язык очень хорошо интегрируется в .
NET. Достаточно посмотреть несколько примеров и убедиться, насколько лаконичен код. Это, на мой взгляд, серьезный аргумент — компактность кода.
Чем опытнее программист, тем больше он должен понимать, что в таких языках, как Java и C#, слишком много церемоний.
Их можно избежать, если сократить код вдвое, поскольку обычно программы на F# в два раза компактнее программ на C#.
— Какие преимущества дает ФП перед ООП? Вагиф Абилов: Первое, как я уже сказал, это отсутствие мутации данных — это очень важно.
Программы, написанные на функциональном языке, не имеют переменных.
В каком-то смысле они сразу оказываются «тяжелыми».
Если вы посмотрите на объектно-ориентированный код, там есть какие-то переменные, какие-то данные, потом они куда-то передаются, и все это доступно из многих потоков.
В функциональных языках поначалу это немного сбивает с толку: как можно работать без введения переменных? Но реализовано все методами «функциональных преобразований».
Именно это создает основу для параллелизма.
Когда вы получаете какие-то данные, вам не нужно отвечать на вопрос: «это потокобезопасно или не поточноЭ» Выдержит ли это доступ из многих потоков или нет? По определению, вы знаете, что выживет. Вам даже не нужно выполнять какие-либо тесты для проверки доступа из нескольких потоков.
За счет отсутствия переменных и того, что вы пропускаете все через функциональные преобразования, тесты значительно упрощаются.
В результате компилятор с гораздо большей вероятностью обнаружит логические ошибки.
Одна из «хороших» проблем F# заключается в том, что я могу часами просто компилировать программу, но когда это произойдет, она будет работать без ошибок.
Это настолько вас убаюкивает, что вы начинаете меньше писать тестов.
Сначала я пытался с этим бороться и писал много тестов как на C#.
Потом я понял, что в этом нет необходимости, поскольку большинство логических ошибок ловит компилятор, который гораздо менее прощает ошибки, чем компиляторы объектно-ориентированных языков.
Пожалуй, это основные преимущества: параллелизм, отсутствие мутаций, большее взаимодействие с компилятором, подверженным логическим ошибкам.
Кроме того, меняется стиль работы.
Если я работаю на C#, я часто использую классическую методологию TDD. С F# я работаю в режиме REPL (цикл чтения-оценки-печати).
Такая работа очень эффективна.
— Есть ли что-то, чего ФП не может сделать по сравнению с ООП? Какие у него (FP) недостатки? Вагиф Абилов: Для каждой задачи должны быть свои средства.
Что касается преимуществ функциональных языков при разработке масштабируемых систем с высокой производительностью, то это понятно и общеизвестно.
Но преимущества функционального программирования при работе с визуальными интерфейсами для меня не очевидны.
Если ваша программа однопоточная и сводится к редактированию форм, то, в целом, здесь будет естественно использовать объектно-ориентированный подход, поскольку формы легче вписываются в модели данных.
F#, Clojure, Erlang также используются для разработки пользовательского интерфейса, но преимущества мне не кажутся очевидными.
Можно также сказать, что обратившись к функциональным языкам, разработчик может решить, что проблемы параллелизма и производительности решатся сами собой, но это никоим образом не заменяет анализ проблем, влияющих на производительность.
Например, об этом нужно подумать разработчику, если он работает с многоядерными процессорами.
Программа должна быть написана так, чтобы использовать кэш процессора.
Производительность может потеряться из-за того, что кэш будет постоянно обновляться.
Это, вообще говоря, задача, не имеющая ничего общего ни с функциональным программированием, ни с объектно-ориентированным программированием.
В любом случае при разработке быстрых масштабируемых проектов необходимо понимать внутреннюю архитектуру систем, на которых они будут работать.
Другими словами, это не панацея; Нельзя ожидать, что обращение к функциональному языку немедленно решит все эти проблемы.
— Подводя итог, на решение каких проблем ориентирован ФП? Вагиф Абилов: Для решения задач, требующих параллелизма и высокой производительности.
В общем, весь бэкенд можно успешно написать на функциональных языках.
— Вагиф, какую инфраструктуру (инструментарий) нужно собрать для реализации проекта на языке ФП? Вагиф Абилов: Поскольку я работаю с C# и F#, Visual Studio — мой наиболее часто используемый инструмент. Но все чаще замечаю, что использую другие, менее «тяжелые» средства.
Например, что касается языка F#, то если мы говорим о .
NET-разработке, то есть Visual Studio Code с плагином Ionide. Это потрясающая среда для работы с F#.
Я бы рекомендовал использовать такие редакторы, как Atom+Ionide, VS Code+Ionide, Paket, Fake. Для тестов есть F#-дружественные фреймворки: FsUnit и библиотека Expecto, которая очень хорошо интегрирована в работу с функциональными языками.
И вот буквально на днях появилась информация о том, что новая IDE JetBrains Rider, которая пока находится в бета-версии, будет поддерживать F#.
Это замечательное развитие, так как JetBrains вообще ребята практичные, и на вопрос о поддержке F# они уже давно отказываются, ссылаясь на сложность встраивания принципов языка в платформу Resharper (насколько я понимаю, трудности связаны с внедрением типов , чего нет в объектно-ориентированных языках).
Но лед сломался, F# стал слишком важным языком в среде .
NET, чтобы его можно было продолжать игнорировать.
Если вам нужно написать веб-приложение, есть замечательный фреймворк Suave. Он позволяет очень компактно, буквально в несколько строк, написать веб-приложение или веб-сервис.
Если говорить о реализации микросервисов, то функциональные языки очень хорошо работают вместе с Actor-моделью.
Последние полтора года я разрабатываю систему на F# с использованием Akka.Net, в которой реализована эта модель.
Помимо прочего, важными компонентами будут поставщики типов, которые реализованы на F# и позволяют очень эффективно работать с базами данных.
Они заменяют тяжеловесные библиотеки, такие как Entity Framework. Кстати, интересный пример.
Существует библиотека F# с открытым исходным кодом под названием SQLProvider, которая необычна тем, что включает семь драйверов в одном модуле: MSSQL, Oracle, SQLite, PostgreSQL, MySQL, MsAccess, ODBC. И все это весит всего 1,3 мегабайта.
А каждый драйвер базы данных — это примерно 600–800 строк кода.
Речь, кстати, о том, насколько компактно можно написать многие вещи на F#.
— Есть ли у вас лично большие и серьезные проекты, реализованные с использованием функционального программирования? Вагиф Абилов: Да.
Последние полтора года небольшая группа пишет систему на F# с использованием Akka.Net, к которой предъявляются высокие требования к производительности и масштабируемости.
Эта система разрабатывается для норвежского общественного радио и телевидения.
Он обрабатывает многие сотни терабайт файлов, работая с облаком.
Код получается очень компактным, несмотря на сложность системы.
— Как вы думаете, ФП станет достаточно популярной, чтобы составить конкуренцию ООП? Вагиф Абилов: Что касается конкуренции, то функциональное программирование уже успешно конкурирует с объектно-ориентированным программированием и заменяет его во многих проектах.
Если мы говорим о количественном сравнении, то надо понимать, о какой временной перспективе мы говорим.
Вероятно, в ближайшие пять лет ФП не дойдет до ряда проектов, сопоставимых с ООП, по разным причинам.
Один из них заключается в том, что начать изучать программирование с объектно-ориентированных языков гораздо проще.
Плюс есть большое количество задач с пользовательским интерфейсом, где, как я уже говорил, польза от ФП не очевидна.
Мне кажется, что большие масштабируемые системы все чаще будут писаться на функциональных языках.
Одна из причин этого заключается в том, что закон Мура перестает работать.
Если раньше можно было просто дождаться выхода более мощных процессоров, и все само по себе стало бы быстрее, то сейчас этого сделать нельзя.
Необходимо перепроектировать архитектуру в соответствии с существующей производительностью, зная и помня, что она не увеличится.
Это очень большой козырь в пользу функционального программирования.
— Какой совет вы можете дать тем, кто решил начать изучать функциональное программирование? Вагиф Абилов: Я бы посоветовал вам не относиться к этому выбору как к какому-то серьёзному жизненному шагу.
Я заметил, что попытка сменить основной язык программистов считается каким-то радикальным шагом, в отличие, например, от изменения базы данных или какого-то фреймворка.
Например, разработчики JavaScript меняют используемые библиотеки как перчатки.
И это не похоже на какие-то серьезные изменения.
Если вы перешли от реляционной базы данных к базе данных документов, это во многих отношениях более серьезный шаг, чем переход с одного языка .
NET на другой.
Мне как-то довелось пообщаться с ребятами, которые писали систему для одного из заказчиков на F#.
Я спросил, насколько легко убедить заказчика, что вы будете делать проект на F#.
Сказали, что заказчику об этом не сообщили.
В контракте говорилось, что система должна работать под управлением .
NET. На самом деле в этом подходе что-то есть.
Если вы пишете для конкретной операционной среды, то мой совет — будьте максимально активны и старайтесь как можно больше.
Попробуйте другие языки, библиотеки и модели программирования.
Все это пойдет только на пользу.
— О чем будет ваш доклад на конференции DotNext в Санкт-Петербурге в мае? Вагиф Абилов: Мой текущий отчет не будет напрямую связано с функциональным программированием, но в некотором смысле будет связано со сменой парадигмы.
Я буду говорить о том, как разработчик может спроектировать API так, чтобы его было одинаково легко использовать как тем, кто предпочитает типизированное программирование, так и тем, кто использует программирование с динамической типизацией.
Как известно, с появлением .
NET 4.0 появилась возможность встраивать в C# динамические типы (динамический тип).
В каком-то смысле я буду говорить о необходимости быть готовым к смене парадигмы.
И это роднит мой доклад с темой нашего сегодняшнего разговора.
Полная тема доклада Вагифа Абилова, который выступит на нашей конференции DotNext в Питере 20 мая звучит как Типизированный или динамический API? Оба! Мы будем рады видеть вас на этом мероприятии, где нам удалось собрать около 30 замечательных спикеров из разных уголков мира и на самые актуальные темы.
Теги: #программирование #.
NET #Функциональное программирование #F# #dotnext #DotNext #dotnext
-
Опыт Перевозки Компьютера В Самолете
19 Oct, 24 -
Plastic Logic: Технология, А Не Планшет
19 Oct, 24 -
Как Мы Сломали Docshell.ru
19 Oct, 24