Объектно-ориентированное программирование
или
Функциональное программирование
 Публичный пост 8 июня 2020  908

Ну да, эти методологии часто сосуществуют вместе, но батл это битва крайностей 🥊
Так что всё таки
Фабрики и наследование или монады и функторы? Мьютбл или иммьютбл ? Java или Haskell ?

4 аргумента и 27 плюсиков
за «Объектно-ориентированное программирование»
5 аргументов и 53 плюсика
за «Функциональное программирование»
за «Функциональное программирование»

Окей, не всегда. Я не про идиоматичный haskell-код, а про применение принципов ФП к повседневной разработке:

  • делать чистые функции
  • выстраивать их цепочки
  • заворачивать сайд-эффекты в сэндвич

Эти вещи делают поток выполнения более понятным, за кодом проще следить. Микросервисы вот стараются строить по тем же принципам по тем же причинам.

Поэтому многие мейнстрим-языки сейчас внедряют элементы, которые раньше были только во всяких хаскелях: pattern matching, монады (привет, async/await!), рекорды.

Любопытно, я с похожим взглядом на вещи выбрал противоположный вариант. Видимо, не выйдет прям баттла, раз середина настолько размыта.

  Развернуть 1 комментарий
за «Функциональное программирование»
Все больше и больше входит в нашу жизнь

Если раньше corutine, lambda, map, reduce, filter был уделом функциональных языков, то сейчас это втащили даже в джаву. И чем дальше тем больше будет функциональщины в языках.

Причина проста, закон мура уже не работает. Просто посмотрите на процессоры, тот же Ryzen хвастается 128 потоками исполнения, на что UltraSPARC T1 смотрит скептически. Да 15 лет назад был такой же SPARC. Стоил только как SPARC :) Но если мы возьмем прирост производительности ядра, то... современный CPU не далеко ущел от CPU 5 летней давности. Разница в производительности не радикальная.

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

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

Если раньше corutine, lambda, map, reduce, filter был уделом функциональных языков, то сейчас это втащили даже в джаву. И чем дальше тем больше будет функциональщины в языках.
Ну да, эти методологии часто сосуществуют вместе, но батл это битва крайностей

По-моему постановка вопроса предполагает сравнение именно чистого ООП и чистого ФП без учета элементов одного в другом

  Развернуть 1 комментарий

@PmJGRNOozIJestBI, а покажете мне язык с ООП и без примесей ФП? Я вот что-то на вскидку не назову. В том или ином виде будет. Достаточно чтобы язык поддерживал анонимные функции и функции как first class citezen

  Развернуть 1 комментарий

Я не о том. Примеси есть везде, но все равно один язык будет толкать тебя больше в сторону ООП, а второй в сторону ФП, поэтому примеси при сравнении надо не учитывать. Вон прям в заголовке написано: "Java или Haskell?"

Формат баттла такой же ж. Мне баттлы именно этим не очень нравятся, пушшо правильный ответ всегда "да оба варианта правильные блин, и вообще есть середина между ними", но вот там снизу написано "Мы пытаемся выделить аргументы каждой из сторон чтобы потом по ним составить свою картину мира. Рекомендуется избегать аргументов типа «каждый хорош для своего»"

  Развернуть 1 комментарий
за «Объектно-ориентированное программирование»
За функциональщину погундели и забыли

Был момент, когда каждая третья статья в интернете говорила о том, что эра ООП прошла, и в светлом будущем человечество будет писать функциональный код.

Ну и как бывает, хайп кончился, и все кто попробовал хаскель и пр. молодцы, а кто нет, вроде бы все ещё не стали невостребованными специалистами, кек.

Да, говорили они, "в некоторых сферах функциональный код намного лучше, да и вообще в нем нельзя сделать багов", но мы то с вами знаем :))

Ну, так говорили и про МЛ, нейросети и тд. Тогда не зашло, сейчас ок.

  Развернуть 1 комментарий
за «Функциональное программирование»
Удобный параллелизм

Одна из основных идей ООП это сокрытие данных. Это может быть удобно для абстракции, но очень неудобно, когда нужно обеспечивать одновременный доступ. Отсюда и появляются блокировки, дедлоки и race conditions.

ФП с неизменяемостью по умолчанию и данными на виду позволяет параллелить вычисления очень естественно. Часто это даже может сделать компилятор.

  • Концепция сокрытия ортогональна концепции параллелизма.
  • В ООП необязательно шарить состояние и потом его лочить для многопоточного доступа - можно так же как и в ФП делать копии состояния на каждый поток, просто это нужно делать явно.
  • В ООП можно так же как и в ФП писать иммьютабл структуры данных.
  • Не стоит забывать, что everything comes with a cost. Например, создание копий неизменяемых данных не может не увеличивать memory footprint.
  • Если упарываться эффективной утилизацией CPU, то кроме паралеллизма еще важен конкарренси. И тут уже все зависит не сколько от языка, а насколько хорошо реализован скедулер и потоковая модель в платформе/вм. По моему субъективному мнению, в дотнете это сейчас круче и удобнее всего реализовано.

В итоге, все равно: что ООП, что ФП разворачиваются в последовательность инструкций для CPU и сегменты в памяти для простых типов/объектов/структур. И тут уж что ООП, что ФП девелоперам нужно держать в голове мысль: а как оно будет выполняться уровнем ниже? Где и сколько потоков? Где и что кэшируется? Какое время жизни состояния? etc.

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

  Развернуть 1 комментарий

Вон, Erlang - конкуррентнее некуда. Но концептуально модаль акторов - это вполне себе объектики в стиле Smalltalk, только с композицией вместо наследования.

  Развернуть 1 комментарий

@hackman,

Например, создание копий неизменяемых данных не может не увеличивать memory footprint.

Имено для этого был придуман COW

  Развернуть 1 комментарий

@astynax, тоже верно :) В этом батле я не могу встать ни на чью сторону :)

  Развернуть 1 комментарий

@norguhtar, согласен, слово "неизменяемых" тут лишнее.

  Развернуть 1 комментарий
за «Объектно-ориентированное программирование»
Порог вхождения

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

Процитирую своего коллегу: "Как-то я кодил на Scala, и из-за количества закрывающих скобочек код выглядит так, будто кто-то от души поугорал".

Говорят, человек не может за раз удержать в голове более ~7 элементов. То есть, цепочка вызовов функций, по-хорошему, не должна превышать это число — код становится трудным для понимания. Добавьте сюда человеческую лень и склонность мозга всячески экономить энергию, и это число сократится ещё на пару пунктов.

Часто от приверженцев ФП можно слышать аргумент а-ля "ты просто туповат(а) для ФП". Да, простите, я не bigdata-ml-highload-faang-engineer, но далеко не во всех областях это нужно.

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

Хорошее понимание ООП внезапно сложнее, чем ФП.

  Развернуть 1 комментарий

@norguhtar, не совсем понимаю, что вы имеете в виду. Если мы говорим про понимание парадигмы как таковой, то "мыслить объектами" человеку проще, чем в терминах формальной математики.

Если же мы говорим про код, написанный в одной из парадигм, то тут и да, и нет. С помощью ООП действительно можно так "наабстрагироваться", что поди разбери, что этим хотел сказать человек. Здесь действует тот же принцип — чем больше слоёв и абстракций, тем сложнее.

На самом деле, на ООП можно вполне себе наколхозить нечто похожее на ФП, например, на проксях и декораторах. Особенно если присмотреться к идеологии Егора Бугаенко с его class If и прочими прелестями на тех же декораторах. Но вот бОльшая часть комьюнити смеётся над его "лесенками" хотя бы потому, что получается нечитаемый адок.

Но давайте всё же вспомним о том, что функциональное программирование лишает нас операции присваивания, т.е. мы говорим об имутабельности. Здесь, как всегда, нужно задаваться вопросом зачем нам это нужно. Мы наше программирование используем в 90% случаев поверх какого-либо хранилища, со всякими апдейтами, проставлениями статусов и проч. И в таком мире куда проще и понятнее поменять значение поля, нежели заново "вычислять" такой же объект с другим полем. И вот тут встаёт вопрос, а стоит ли тратить время и задумываться над тем, что же делает эта цепочка функций, чтобы в итоге прийти к озарению "да она же просто поле меняет"?

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

Ну и да, имутабельность можно и в ООП замутить :)

  Развернуть 1 комментарий

@efitapia,

не совсем понимаю, что вы имеете в виду. Если мы говорим про понимание парадигмы как таковой, то "мыслить объектами" человеку проще, чем в терминах формальной математики.

Только пока у вас немного объектов. Как только объектов становится много приходится распиливать их на черные ящики которые могут обозреваться человеком. Либо иметь мозг с размером с дом.

И внезапно это метод из той же математики. И его можно выразить через функцию.

На самом деле, на ООП можно вполне себе наколхозить нечто похожее на ФП, например, на проксях и декораторах

Вот не надо. Лучшее что в свое время там случилось это DI и декораторы. Это позволило избавиться от муторной инициализации всего и вся и перейти по сути на деклативное программирование. Вот не поверите, но я настоящего ООП кода с наследованиями больше 2 не писал с универа. В большинстве своем выливается возьми интерфейс, напиши реализацию подпихни через DI куда надо.

На классическое ООП это похоже очень мало :)

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

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

А так же позволяет тестировать функцию отдельно от окружения.

наше программирование используем в 90% случаев поверх какого-либо хранилища, со всякими апдейтами, проставлениями статусов и проч. И в таком мире куда проще и понятнее поменять значение поля, нежели заново "вычислять" такой же объект с другим полем.

Угу особенно прекрасно в случае если вы используете ORM. И там вот ActiveRecord внезапно не выстрелил. По большей части все используют Data-Mapper. Где у нас есть отдельно объект и отдельно слой который данные модифицирует. В итоге все выливается в подготовить объект, пихнуть его в слой работы с БД.

Хммм что-то это мне напоминает. Например функциональный подход.

Почему такое чаще используют? Проще подменить реализацию логики, которая подменять реализацию ActiveRecord.

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

И делает несчастными математиков которые говорят это же равно, оно не может присваивать! Что вы делаити!

Ну и да оператор присваивания никуда при этом не девается. Просто у вас нет оператора изменения объекта, а только создание нового с новыми свойствами. Зато и нет сайд-эффектов.

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

  Развернуть 1 комментарий

@norguhtar, добавление "слоя, как у них", позволяет иметь и иммутабельность (когда надо), и сайд-эффекты (опять-таки когда надо). Пуританство в языках, на мой взгляд, неуместно.

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

  Развернуть 1 комментарий

@EduardSurov,

добавление "слоя, как у них", позволяет иметь и иммутабельность (когда надо), и сайд-эффекты (опять-таки когда надо).

Главное чтоб слой работал корректно и не добавлял своих сайд-эффектов.

Вообще замечал, что радикальные адепты ФП триггерятся на оператор присваивания

НИЗЗЗЯ!

При этом я не пишу на чистом ФП. Основные языки на которых я пишу сейчас это js и lua :) Которые конечно включают ФП, но и зайчатки ООП там есть.

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

  Развернуть 1 комментарий

@norguhtar, вы знаете, мне кажется, что понятие "настоящего, классического ООП" — это уже что-то из области теологии, мне здесь нечего вам ответить.

В итоге все выливается в подготовить объект, пихнуть его в слой работы с БД. Хммм что-то это мне напоминает. Например функциональный подход.

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

Что касается имутабельности, то конечно же я не могу не согласиться, что это круто и во многом удобно, но не всегда. И вот тут @EduardSurov верно заметил, что мне, как разработчику, хочется самостоятельно решать, когда использовать имутабельность, а когда нет, и чтобы это не было ограничено "базисом" ЯП.

Вообще говоря, заимствование и подражание — это не плохо. Дуров вон спёр идею FB и Whatsapp-а, и получилось же лучше :D

Тогда уж сразу стоит писать на ФП

Стоит писать так, как того требует ситуация. Иногда, прости господи, ситуация позволяет всё запихать в God Object , но мы всё же хотим погибче, поэтому используем ООП, а где удобно — применим лямбды. Этим и прекрасны мультипарадигмальные ЯП.

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

  Развернуть 1 комментарий

@efitapia,

вы знаете, мне кажется, что понятие "настоящего, классического ООП" — это уже что-то из области теологии, мне здесь нечего вам ответить.

Ну это там Гради Бутч и прочее :) Предания старины глубокой, когда все в ручную закатывали,а не как щас фигак фигак и в продакшон.

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

Тут конечно я буду за java говорить. Но POJO не только там есть. Мы передаем в функции сервисного слоя объект, который скорее структура, а не объект. А дальше он обрабатывается внутри. Понятное дело что все у друг друга тырят :)

И вот тут @EduardSurov верно заметил, что мне, как разработчику, хочется самостоятельно решать, когда использовать имутабельность, а когда нет, и чтобы это не было ограничено "базисом" ЯП.

Главное чтобы это не выстрелило в неожиданном месте. Такое часто выстреливает в тех языках, где используется ссылочная модель. Хороший пример js.
Там я если собираюсь что-то менять всегда использую создание нового объекта или функции порождающие новые объекты. Это тупо безопаснее в контексте этого языка. В какой-то момент ты просто привыкаешь к такой модели и все.

Этим и прекрасны мультипарадигмальные ЯП.

Но склоняюсь я к тому, что для большинства случаев ООП — приемлемый трейд-офф.

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

Вообщем в итоге и докатились, до того что надо знать и то и то. Причем замечу, что лет 10 назад это так сильно развито не было.

  Развернуть 1 комментарий

@norguhtar, ну в js много что выстрелить может :D

  Развернуть 1 комментарий
за «Функциональное программирование»
В современном мире слишком много неопределенности для сложных систем типов

Я за функциональные языки с динамической типизацией (Racket, Clojure) или императивные со статической, которые не имеют механизма наследования, а только композиции (Go). Разработчики не имеют достаточно контекста, чтобы пользоваться ООП и функциональными языками со строгой статической типизацией корректно. Попытки все типизировать и определить границы взаимодействия обычно не имеют смысла в большей части проектов, особенно на ранней стадии. Вам повезло, если у вас легко формализуемая область или вы знаете ваш домен настолько, чтобы попытаться использовать ООП или ML-подобное ФП.

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

Динамические языки тоже неявно предполагают какие-то типы, но не дают инструментов следить за изменениями.

  Развернуть 1 комментарий

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

  Развернуть 1 комментарий

@holdn, в случе clojure у вас там список только и примитивы. А дальше вертись как хочешь :)

  Развернуть 1 комментарий

Так, стоп :) Динамическая vs Статическая типизации - это другой батл :)

  Развернуть 1 комментарий

@norguhtar, про множества, вектора, мапы искоробочные вы почему-то умолчали. А между прочим чуть более чем весь код на Clojure, это жонглирование мапами. Даже Clojure Spec в большинстве своём именно для спецификации maps используют.

  Развернуть 1 комментарий

@astynax, это лисп вид с боку. В лиспе все список.

  Развернуть 1 комментарий

@norguhtar, Clojure - hosted язык, у которого весь stdlib завязан на платформу (Java или JS). Это не "десяток специальных форм и всё остаольное собрано из них", как в честных лиспах.

И "всё есть список" не соответствует истине - и мапы, и вектора написаны на языках платформ.

  Развернуть 1 комментарий
за «Функциональное программирование»
Можно выпендриваться перед пацанами на ∏ρ؃uñçτØρ Øπτµç∑

И даже во дворе

за «Объектно-ориентированное программирование»
Меньше выламывает мозг

ФП и его элементы отлично работает в отдельных областях приложения, но в других превращается в write-only code. Современные практики берут в себя лучшее из обоих миров; но в целом в любой непонятной ситуации лучше начинать с ООП.

за «Объектно-ориентированное программирование»
🌵 Патаму что! Как еще лично я мог ответить, лол:)

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

Есть такая штука как "three layer cake". "Трехслойный пирог" - ИМХО единственно верная высокоуровневая архитектура приложений. Все остальные методики - порты-адаптеры, чистая архитектура, луковка, гексагональная - либо неявно повторяют трехслойный пирог используя другую терминологию, либо провальны.

Статья написана по Хаскелю, но потенциально, на самом деле, идея применима абсолютно для любого стека. И на мой субъективный взгляд, послесловие этого поста поясняет очень иллюстративно, что есть где и зачем.

😎

Автор поста открыл его для большого интернета, но комментирование и движухи доступны только участникам Клуба

Что вообще здесь происходит?


Войти  или  Вступить в Клуб