«Паттерны» функционального программирования
Многие люди представляют функциональное программирование как нечто очень сложное и «наукоемкое», а представителей ФП-сообщества – эстетствующими философами, живущими в башне из слоновой кости.
До недавнего времени такой взгляд на вещи действительно был недалек от истины: говорим ФП, подразумеваем Хаскель и теорию категорий. В последнее время ситуация изменилась и функциональная парадигма набирает обороты в web-разработке, не без помощи F#, Scala и React. Попробуем взглянуть на «паттерны» функционального программирования, полезные для решения повседневных задач с точки зрения ООП – парадигмы.
ООП широко распространено в разработке прикладного ПО не одно десятилетие. Все мы знакомы с SOLID и GOF. Что будет их функциональным эквивалентом. Функции! Функциональное программирование просто «другое» и предлагает другие решения.
Основные принципы функционального проектирования (дизайна)

Функции как объекты первого класса
Синтаксис F# подчеркивает, что функции и значения равны в правах:
Композиция как основной «строительный материал»
Можно представлять себе композицию функций как фрактал. Определение фрактала в строгом смысле не совпадает с определением композиции. Представляя фрактал вы можете визуализировать как ваш control flow состоит из скомпонованных функций, состоящих из скомпонованных функций, состоящих из…
Шаблон компоновщик (Composite) в ООП тоже можно представлять себе «фракталом», но компоновщик работает со структурами данных, а не преобразованиями.
Типы тоже можно компоновать. Большая часть функциональных ЯП работает с алгебраической системой типов, отличающейся от системы классов в ООП.
Перемножение (логическое «и», record type в F#)
На первый взгляд это может показаться странным, однако в этом есть смысл. Если взять множество людей и множество дат, «перемножив» их мы получим множество дней рождений.
Сложение (логическое «или», discriminated union type в F#)
Discriminated union – сложное название. Проще представлять себе этот тип как выбор. Например, вы можете на выбор оплатить товар наличными, банковским переводом или с помощью кредитной карты. Между этими вариантами нет ничего общего, кроме того, все они являются способом оплаты.
Однажды нам пригодились «объединения» для моделирования предметной модели.
Entity Framework умеет работать с такими типами из коробки, нужно лишь добавить id.
Стремление к «полноте»
ФП подталкивает вас к более строгому и полному описанию сигнатур функций. Если функции не выбрасывают исключений вы можете использовать сигнатуру и систему типов в качестве документации. Вы также можете использовать систему типов для создания предметной модели (Domain Model) и описания бизнес-правил (Business Rules). Таким образом можно гарантировать, что операции не допустимые в реальном мире не будут компилироваться в приложении, что дает более надежную защиту, чем модульные тесты. Подробнее об этом подходе вы можете прочитать в отдельной статье.
Функции в качестве аргументов
Хардкодить данные считается дурным тоном в программирование, вместо этого мы передаем их в качестве параметров (аргументов методов). В ФП мы идем дальше. Почему бы не параметризировать и поведение?
Вместо функции с одним аргументом опишем функцию с двумя. Теперь не важно, что это за список и куда мы выводим данные (на консоль или в лог).
Пойдем дальше. Рассмотрим императивный пример на C#. Очевидно, что в данном коде присутствует дублирование (одинаковые циклы). Для того чтобы устранить дублирование нужно выделить общее и выделить общее в функцию:
В F# для работы с последовательностями уже есть функция fold:
Рекомендую цикл статей Эрика Липперта о монадах в C#. С десятой части начинается объяснение «монадической» природы SelectMany
Функции в качестве интерфейсов
Допустим у нас есть интерфейс.
Если взять SRP и ISP и возвести их в абсолют все интерфейсы будут содержать только одну функцию.
Паттерн «декоратор» реализуется с помощью композиции функций
Здесь автор для простоты изложения опускает вопросы семантики. При моделировании реальных предметных моделей одной сигнатуры функции не всегда достаточно.
Каррирование и частичное применение
Итак, использую одну только композицию мы можем проектировать целые приложения. Плохие новости: композиция работает только с функциями от одного параметра. Хорошие новости: в ФП все функции являются функциями от одного параметра.
Такие преобразования возможны не только для компилируемых функций в программировании, но и для математических функций. Возможность такого преобразования впервые отмечена в трудах Готтлоба Фреге, систематически изучена Моисеем Шейнфинкелем в 1920-е годы, а наименование получило по имени Хаскелла Карри — разработчика комбинаторной логики, в которой сведение к функциям одного аргумента носит основополагающий характер.
Возможность преобразования функций от многих аргументов к функции от одного аргумента естественна для функциональных ЯП, поэтому компилятор не будет против, если вы передадите только одно значения для вычисления суммы.
Это называется частичным применением. В функциональных ЯП частичное применение заменяет принцип инъекции зависимостей (Dependency Injection)
Продолжения (continuations)
Зачастую решения, закладываемые в реализацию, оказываются не достаточно гибкими. Вернемся к примеру с делением. Кто сказал, что я хочу, чтобы функция выбрасывала исключения? Может быть мне лучше подойдет «особый случай»
Вместо того, чтобы решать за пользователя, мы можем предоставить решение ему:
Если вы когда-нибудь писали асинхронный код, то наверняка знакомы с «пирамидой погибели» (Pyramid Of Doom)
Продолжения позволяют исправить этот код и избавиться от уровней вложенности. Для этого необходимо инкапуслировать условный переход в функцию:
И переписать код, используя продолжения
Монады
Монады – это одно из «страшных» слов ФП. В первую очередь, из-за того, что обычно объяснения начинаются с теории категорий. Во вторую — из-за того что «монада» — это очень абстрактное понятие, не имеющее прямой аналогии с объектами реального мира. Я большой сторонник подхода «от частного к общему». Поняв практическую пользу на конкретном примере проще двигаться дальше к более полному и абстрактному определению.
Зная о «продолжениях», вернемся к аналогии с рельсами и тоннелем. Функцию, в которую передаются аргумент и два «продолжения» можно представить как развилку.
Но такие функции не компонуются 🙁
На помощь приходит функция bind
Код пирамиды погибели может быть переписан с помощью bind
Кстати, это называется «monadic bind». Скажите своим друзьям, любителям хаскеля, что вы знаете, что такое «monadic bind» и вас примут в тайное общество:)
Bind можно использовать для сцепления асинхронных операций (промисы в JS устроены именно так)
Bind для обработки ошибок
Рассмотрим код на C#. Он выглядит достаточно хорошо: все кратко и понятно. Однако в нем отсутствует обработка ошибок. Действительно, что может пойти не так?
Мы все знаем, что обрабатывать ошибки нужно. Добавим обработку.
Вместо шести понятных теперь 18 не понятных строчек. Это 200% дополнительных строчек кода. Кроме того, линейная логика метода теперь зашумлена ветвлениями и ранними выходами.
С помощью bind можно абстрагировать логику обработки ошибок. Вот так будет выглядеть метод без обработки ошибок, если его переписать на F#:
А вот этот код но уже с обработкой ошибок:
Более подробно эта тема раскрыта в отдельном докладе.
Функторы
Мне не очень понравилось описание функторов у Скотта. Прочитайте лучше статью «Функторы, аппликативные функторы и монады в картинках»
Моноиды
Я предупредил, итак, математика
И еще немного
Что общего между этими примерами?
За более строгим определением обратитесь к википедии. В рамках статьи обсуждается лишь несколько примеров применения моноидов на практике.
Замкнутость
Дает возможность перейти от попарных операций к операциям на списках
Ассоциативность
Нейтральный элемент
Кстати, в математике часто встречается определение моноида как полугруппы с нейтральным элементом. Если нейтральный элемент отсутствует, то можно попробовать его доопределить, чтобы воспользоваться преимуществами моноида.
Map / Reduce
Если ваши объекты — не моноиды, попробуйте преобразовать их. Знаменитая модель распределенных вычислений Google — не более чем эксплуатация моноидов.
Эндоморфизмы
Монады VS моноиды
Кстати, бастион ООП — GOF тоже содержит монады. Паттерн «интерпретатор» — это так называемая свободная монада.
Stop exercising with joint pain
Make Gains Without The Pains
We Take the Guesswork Out of Taking Care of Your Body!
Get started, learn about the
10 week online course
WHAT IS FUNCTIONAL PATTERNS?
Functional Patterns is fitness training designed for humans by humans. By cracking the code on the way we’ve evolved to move, we supercharge your body’s ability to move better, and teach you how to train without all the pains that typically come with other training methods.
. through the use of myofascial manipulation and corrective exercise.
. to allow your muscles to work for you and not against you.
. by conditioning your movement and muscle symmetry.
. in a way that simultaneously improves function and performance.
. with increased economy of motion helping you feel great.
. as relief from muscle tension and joint pain get resolved at its root.
From remove chronic pain, to massive weight loss to having a fit, strong, athletic body.
Our results speak for themselves.
Cerebral Palsy Mass And Str.
Improved back pain
Weightloss and Structure Gains

Cerebral Palsy Mass And Structure Gains Without Pains
Rudi’s words: “Since I started to train FP, my mobility and posture has significantly changed, I can do things that I couldn’t do before and my standing feels way more comfortable. For people seeking to function well and improve their health, FP is way to go.”
In summary from previous posts, before FP Rudi had been through some health complications, such as spontaneous pneumothorax, femoral head fracture and retinal tear on his eye, because of which he had to undergo a surgery, after which he was told by the doctors that he should never load his body physically anymore. Rudi’s attitude towards the training process has been extremely disciplined, since he has never missed a single week of training (except Covid lockdown period) since his first session.
Improved back pain
POSTURAL IMPROVEMENTS & PAIN FREE RESULTS.
100% Functional Patterns techniques only
Before photo: June 2019
After photo: February 2021
I feel like I am living in a completely different body to what I was back in 2019. Now that I have experienced what it feels like to decompress my joints, I realise how compressed I was from many years of traditional training. The fact that I was exercising at a squat rack most days and doing 3-4 yoga classes a week, but still got constant back pain had me really confused.
On top of that my ability to deal with stress was poor. I was living in a state of anxiety and depression without realising the direct connection to my poor biomechanics.
@functionalpatterns has not only changed the way I feel, look, move and respond to stress, but it has also saved my career in the fitness industry. I am so grateful to @elnaudi for his dedication to making humans functional again and glad that I spend my working day aiming to replicate the same changes on my clients.
If you’re on the fence about FP, my advice would be to just start. It’s the best decision I ever made.


Weightloss and Structure Gains
functionalpatterns WEIGHTLOSS AND STRUCTURE GAINS from HBS Practitioner @fp_alex.pennington
9 month resting posture progress from my client Stefan. Since day one, he’s came in to work and it shows. Stefan is hyper mobile which has made structural changes very challenging since he wants to go way past functional ranges of motion. But that hasn’t stopped us, just more reason to build a better more functional body. All changes here done with Functional Patterns methods.
Changes here include:
Будь мужчиной!
Видео с моего канала
Рейтинг постов
Новости сайта
Разделы сайта
Популярные тэги
Архивы статей
Функциональная подготовка бойца от FUNCTIONAL PATTERNS
Я уже писал как-то о комплексе Николая ВДВ с гирькой и гантелей, который меня очень впечатлил своей эффективностью и лаконичностью. Но теперь хочу предложить вашему вниманию нечто более всеобъемлющее и интересное, нечто более эффективное.
Недавно на просторах интернета мой друг и учитель нашел крайне интересный материал по физподготовке бойцов-универсалов, т.е. тех, кому и бить, и бороться нужно. Организация или группа во главе с Naudi Aguilar провозглашает девиз тренировок: «This is not CROSSFIT this is FUNCTIONAL PATTERNS» — «Это не КРОССФИТ, это ФУНКЦИОНАЛЬНЫЕ ПАТТЕРНЫ». И посмотреть действительно есть на что, ничего лишнего и излишне вычурного в системе FUNCTIONAL PATTERNS нет. Все достаточно понятно, просто и в то же время очень эффективно. Я говорю «просто», имея ввиду себя и таких, как я и более физически подготовленных парней и девушек. Если для вас проблема отжаться хотя бы 30-35 раз, подтянуться на перекладине хотя бы раз 7-9, присесть без груза раз 50, то это программа тренинга может оказаться для вас сложной. Хотя если подойти к методике FUNCTIONAL PATTERNS грамотно, то можно набрать кондиции практически с нуля только на этих занятиях.
Единственный минус – для полноценных занятий, что не говори, нужен зал и зал хороший, где установлены все те приблуды, что есть у этого неутомимого парня в видеоролике.
Знаю, что многие люди отдают предпочтение для общей физической подготовки чему-нибудь вроде бега. Достаточно спокойный и монотонный тренинг, можно слушать приятный музон и улучшать тело. Причем модно становится использовать для этих целей эллиптические тренажёры, чтобы прокачивать ещё и руки, а не только нижние конечности.
Но что можно безоговорочно взять для домашней прокачки? Однозначно все, что с гирьками (Kettlebell), можно забирать в копилку любого бойца смешанных единоборств. Кстати, гриф от штанги для движений на повороты, можно заменит просто ломом. Работа с большим мячом также легко переносится в домашний формат. А вот уж всякие там стойки металлические хитрые и гремящие упражнения типа ударов медицинбола об пол со всего размаху, дома делать не советую, для этого есть либо природа, либо зал, пол в котором специально предназначен для всяческих нагрузок с топаньем и падением.
Старые, проверенные упражнения и оправданные инновации в тренинге создают стальное тело в кратчайшие сроки. Советую ознакомиться с данным направлением тренинга, причем лучше всего взять на заметку минимум несколько упражнений и следовать рекомендациям этого заводного парня. Многие из его упражнений я уже видел, но именно в таком сочетании их не было нигде. Кстати, довольно ещё интересный курс по нагонке физухе для боксеров, кикбоксеров и ммашников есть на рутрекере под названием SAID.
выглядит прикольно,но по моему это просто своя свободная инерпретация физ.упражнений. думаю любой человек при определенной подготовке может создавать какие то свои ремиксы классических упражнений.
Азамат,у тебя опечатка в заголовке статьи)Исправил. Спасибо.
Паттерны Command и Strategy с точки зрения функционального программирования
Паттерны проектирования и функциональное программирование? Как это вообще связано?
В умах многих разработчиков, привыкших к объектно-ориентированной парадигме, возникает впечатление, что проектирование программного обеспечения, как таковое, неразрывно связано с ООП и всё остальное — суть ересь. UML, большей частью нацеленный на ООП, используется как универсальный язык для проектирования — хотя он таким, конечно, не является. И мы видим, как мир объектно-ориентированного программирования постепенно погружается в пучину преступного переинженеринга (1).
В силу этого зачастую даже не ставится вопрос о выборе парадигмы программирования. Тем не менее, этот вопрос является весьма существенным, и зачастую правильный ответ даёт большие преимущества (3). Это, вообще говоря, выходит за рамки того, что мы привыкли называть проектированием — это вопрос из области архитектуры.
Лирическое отступление: разница между архитектурой, проектированием и реализацией
Не так давно я наткнулся на весьма интересное исследование — (2). В нём рассматривается задача формализации понятий «архитектура», «проектирование» и «реализация», которые чаще всего употребляются неформально. И авторам удаётся вывести весьма интересный критерий: критерий Intension/Locality. Я не буду углубляться в философию и просто приведу краткое описание критерия (эта часть — фактически перевод) и мои выводы из него.
Свойство Intension (интенсионность) означает способность некой сущности описывать бесконечное множество предметов: например, понятие простого числа. Ему противоположно свойство экстенсионности — сущность описывает конечный набор предметов: например, понятие страны — члены НАТО.
Свойство локальности — сущность влияет только на отдельную часть системы. Соответственно, глобальность — сущность влияет на всю систему в целом.
Дак вот, учитывая эти два свойства, авторы указанного исследования составляют такую таблицу: 
Пользуясь ей легко определить, что относится к уровню архитектуры, а что — к уровню проектирования. И вот мой вывод: выбор парадигмы программирования, платформы и языка — это решение уровня архитектуры, т.к. этот выбор глобален (затрагивает все части системы) и интенсионен (парадигмы определяют способы решения бесконечного множества задач).
Тем не менее, решить столь глобальную задачу (найти критерии выбора подходящей парадигмы) мне пока не по силам. Поэтому я решил выбрать два уже существующих класса задач и показать, что для них стоит использовать не привычный для многих ОО подход, а функциональный, который в последнее время приобретает (заслуженно) всё большую популярность.
Классы задач я выбрал необычным методом — я взял два паттерна ОО проектирования и показал, что они, по сути — ограниченная реализация понятия из области функционального программирования — функции высшего порядка (higher-order function, далее: ФВП). Гипотеза заключалась в том, что паттерны — это устоявшиеся решения определённых проблем, а раз возникают проблемы и их устоявшиеся решения, видимо есть некие слабости и недостатки, которые приходиться преодолевать. Для рассмотренных паттернов это действительно так.
Кстати говоря, подобный подход был использован в (5) и (6). В (6) вообще было указано на возможность замены большинства паттернов, но подробный анализ каждого не проводился. В (5) было более подробное рассмотрение Command и Strategy, но немного с другой стороны. Я решил сделать что-то более практичное, чем в (6), и с другими акцентами, чем в (5). Итак, приступим.
Higher-order functions
Думаю, практически все в той или иной форме знакомы с этой идей.
Функция высшего порядка – это функция, которая принимает в качестве аргумента или возвращает как результат другую функцию.
Такое становится возможным благодаря основной концепции функционального программирования: функции – это значения. Стоит отметить, что когда мы говорим, что функция и значение в функциональном программировании полностью соответствуют аналогичным понятиям из математики, мы имеем в виду именно полное соответствие. Это одно и то же. Пример широко распространённых в математике ФВП – операторы дифференцирования, интегрирования и композиции (вообще говоря, это близко к понятию оператора из функционального анализа). Оператор композиции имеет непосредственное выражение в большинстве языков, поддерживающих функциональную парадигму. Пример на F#:
Очевидно, что запись f Execute() LoggingInvoker‘а, так и в самом LoggingInvoker‘e — выбор зависит от конкретной ситуации):
А вот как мы их будем отменять:
performedOps и undoneOps — это стеки, хранящие исполненные и отменённые команды.
Однако, после рассмотрения ФВП довольно очевидно, что всё это поведение можно реализовать в виде ФВП, если выбранный язык поддерживает такую возможность. Действительно, объект Invoker можно заменить ФВП, принимающей в качестве аргумента функцию, соответствующую конкретной операции – нам больше не нужны объекты Command, потому что функции сами являются значениями, и интерфейс ICommand, т.к. его функции выполняет система типов языка, поддерживающего функциональную парадигму.
Приведём схему замены этого паттерна на конструкцию в функциональной парадигме, могущей выполнять те же функции:
На псевдокоде (инспирированном F#) соответствующая функциональная реализация будет выглядеть так:
Паттерн Strategy
Паттерн Strategy предназначен для того, чтобы позволить клиенту выбирать один из нескольких возможных путей решения задачи. В ООП для этого создаётся следующая конструкция:
Контекст хранит в себе ссылку на одну из реализаций интерфейса IStrategy, при необходимости выполнить некую операцию он обращается к методу этого хранимого объекта. Меняя объекты — меняем методы.
Это так же легко преобразуется к функциональному стилю. На этот раз мы можем использовать список функций, чтобы сохранить возможные стратегии:
Выводы
1. Правильный выбор парадигмы в соответствии с классом решаемой задачи зачастую может быть критическим фактором для успешности её решения. Если ваша задача относится к классу т.н. behavior-centric, стоит задуматься об использовании функционального подхода.
2. Паттерны Command и Strategy — это ограниченная реализация функций высшего порядка
3. Не обязательно переходить на чисто функциональный язык, чтобы использовать преимущества решения с помощью ФВП — большинство современных мейнстримовых языков в той или иной мере поддерживают ФВП.
В последнее время появилось большое количество языков, в одинаковой мере сочетающих ОО и функциональную парадигму, многие ОО языки начали приобретать функциональные возможности. Надеюсь, что кому-то эта статья поможет лучше использовать новые возможности их любимых языков программирования. Успехов в работе!
Источники
1. Criminal Overengineering
2. Architecture, Design, Implementation. Amnon H. Eden, Rick Kazman. Portland: б.н., 2003. 25th International Conference on Software Engineering — ICSE
3. Banking Firm Uses Functional Language to Speed Development by 50 Percent. Microsoft Case Studies. март 2010 г.
4. Bishop, Judith. C# 3.0 Design Patterns. Sebastopol, California: O’Reilly, 2008.
5. Tomas Petricek, Jon Skeet. Functional Programming for the Real World. б.м.: Manning Publications, 2010.
6. Gabriel, Richard P. Objects Have Failed Slides DreamSongs.com.
7. Smith, Chris. Programming F#. Sebastopol, California: O’Reilly, 2010.
UDP:
alexeyrom написал очень полезный комментарий, с его согласия выношу в тело поста, чтобы было видно:
«Норвиг в 1996 году рассматривал паттерны в Lisp и Dylan. Собственно, результат аналогичный (многие паттерны становятся тривиальными или существенно упрощаются), но на более богатом материале.»












