Разговор со спикерами FPConf

Привет!

Конференция FPConf уже в эту сууботу, нас аж 160 и еще не поздно заскочить в последний вагон. Регистрация — тут.

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

image

В объектно-ориентированных языках есть широко известный список паттернов проектирования (design patterns) от «Банды четырех» (Gang of Four). В функциональных языках такого известного списка не существует. С вашей точки зрения, почему так?
Подобные паттерны не нужны при программировании на функциональных языках или просто их канонический список еще не сложился?

Антон Холомьев, Haskell
Для Haskell такой список есть, но он по-другому называется и там другие паттерны,
в стиле ФП. Тут

Сергей Лобин, Scala
На мой взгляд, паттерны — это принципы FP, которыми можно пользоваться как кубиками, собирая прекрасные программы :)

Сергей Тихон, F#
В вопросе паттернов для языка программирования я солидарен с Peter Norvig который сказал что «Design patterns are bug reports against your programming language.». Большая часть проблем которые решали классические паттерны от Gang of Four уже решена в функциональных языках в том или ином виде. «Функциональные паттерны\абстракции» есть и будут появляться (например Монады), как набор общих подходов для управления сложностью разрабатываемых приложений. Возможно со временем появится один общий список который мы все признаем как канонический, но это будет вызвано заметный увеличение сложности задач которые позволят решать функциональные языки.

Александр Гранин, Haskell
Вопрос очень коррелирует с темой моего доклада. И я его несколько раз поднимал на наших встречах LambdaNsk.

Паттерны проектирования в ООП — это искусственные конструкции, решающие ту или иную техническую проблему, которую нельзя решить естественным путем — синтаксисом языка, верхнеуровневыми идиомами или концепциями. Значительная часть паттернов (если не все) оперирует ООП-понятиями «наследование», «полиморфизм», «абстракция», в то время как ни эти понятия, ни какая иная конкретная синтаксическая конструкция ООП-языка не решают проблему напрямую. В то же время в функциональных языках эти паттерны либо вырождаются (Visitor), либо становятся просто не нужны (Command) — в виду того, что в самом ФП есть концепции, которые непосредственно могут решить проблему: ФВП, лямбды, сопоставление с образцом, первоклассные функции, иммутабельность, композиция, ленивость и многое другое. Например, Visitor легко заменяется сопоставлением с образцом и ФВП, а Command — просто первоклассными функциями. В ООП же паттерны — это сложные конструкции, не присущие самому ООП-языку. То есть, чтобы решить проблему, ООП-языка и его элементов недостаточно: нужно составить из этих элементов тот или иной механизм. А много похожих механизмов в итоге и обобщаются в ООП-паттерны. Напротив, чтобы в ФП решить проблему, достаточно только языковых конструкций, и зачастую хватает просто составить тип функции. Если есть тип — реализация функции будет уже простой.

В то же время, в ФП существуют свои «паттерны», хотя я предпочитаю называть их «идиомами». Вы о них слышали: разные монады, комонады, функторы, стрелки, аппликативные функторы, комбинаторы, трансдьюсеры. Кроме того, в ФП есть такие паттерны как FRP, STM, линзы. В чем, по моему мнению, отличие ООП-паттернов от ФП-идиом?

ООП-паттерн — это решение проблемы «снаружи» императивным подходом. ООП-паттерн адресует к сущностям и их мутабельному взаимодействию. ООП-паттерн описывает, «как работает» система. Бывают ООП-паттерны, которые нужны, чтобы лишь реализовать недостающую в языке функциональную идиому (Visitor — это сопоставление с образцом и ФВП).

ФП-идиома — это решение проблемы «изнутри» функционально-декларативным подходом. ФП-идиома адресует к свойствам и их немутабельной трансформации; ФП-идиома описывает, «чем является» сущность, какие у нее есть неотделимые свойства.

О каких свойствах речь? Например, если у вас есть функциональный список, — то он изначально, даже без вашего знания об этом, является монадой. А если у вас есть игра «Жизнь», то ее клеточное поле уже является комонадой. Из этого вытекает, кстати, что ФП-программист не просто конструирует решение проблемы, — он ищет в предметной области скрытые характеристики, свойства, и уже исходя из этих знаний, решает проблему, применяя ту или иную ФП-идиому. Такой код — построенный на свойствах и трансформации — и будет являться идиоматичным функциональным кодом.

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

Ссылки по теме
* Заменяет ли ФП GOF-паттерны?
* Паттерны проектирования в Haskell
* Паттерны проектирования в ФП
* Идиоматичный функциональный код (презентация с моего выступления на LambdaNsk)
* Идиомы в Haskell и ФП: 1 и 2

Денис Редозубов, Haskell
+1 к предыдущему ответу.

Николай Рыжиков, Clojure
Не все паттерны GOF одинаковы. Некоторые слишком низкоуровневые — например, Iterator. В ФП есть свой (более выразительный) арсенал для подобных задач: map, reduce/fold, walker. А также ряд сугубо функциональных низкоуровневых шаблонов: monads, zippers, transducers.

Однако, значительная часть шаблонов — это полезные и понятные опытным программистам высокоуровневые конструкции, позволяющие структурировать и описывать программу in the large. И они вполне могут использоваться в ФП, иногда с несколько отличной от оригинала реализацией.

Например, Chain of Responsibility может быть выражен через HOF как декорация функций (используем js для доступности:):

function handler1(next_handler) {
  return function(args){
    if (exp){    // some logic
     next_handler(args); //pipe to next handler
    } else {
     return some_responce; //intercept
    }
  }
}

var stack = handler1(handler2(handler3)) // build chain (stack)
stack(args); // process chain

Поэтому думаю надо просто перевыпустить Шаблоны Проектирования Для ФП. Возможно, вы тот, кто напишет эту книгу :)

Никита Прокопов, Clojure
Паттерны есть и там, и там. ООП-язык говорит вам: вот есть объекты, делайте что хотите. Свобода, но неконструктивная. Паттерн же это «вот если вы сконструируете объект по такой схеме, его можно использовать для таких-то целей». Конкретизация.

Поэтому я бы не стал заходить так далеко, чтобы приписывать ФП-языкам некие особые свойства, аннулирующие паттерны. Паттерн — это просто классификатор часто встречающихся «форм кода». Конкретные ООП-паттерны из GoF не нужны, потому что нет объектов. Но ФП-паттерны нужны. Как-то ведь ФП код пишут, и он не то чтобы у каждого человека уникальный.

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

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

Михаил Лиманский, Scala
Функциональщикам паттерны не нужны, потому что мы люди творческие, а не ремесленники.

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

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

Апдейты от остальных участников и спикеров конференции — в комментариях. До встречи на FPConf!

© Habrahabr.ru