Обновить

Комментарии 285

Для меня актуальная тема — напоминает о том, что за модными терминами часто теряется суть изначально гениальных идей. Особенно развенчание мифов вокруг Scrum приятно видеть.

только вот изначальное гениальное идея не кому нужно было а вот это извращение...

А в чем тут гениальность, извините?

Автор не разобравшись в сути SCRUM, рассуждает о том, что недельными спринтами работать нереально... Выход - перейти на месячные спринты. Приводя при этом нелепые аргументы в духе "ну как мы можем сделать все за одну неделю?!". Интересно, а как в остальном мире существуют ИТ компании, где выпуск обновлений происходит чуть ли не каждый день... м? Магия наверное.

Автор либо никогда не руководил IT командами, либо никогда не работал в атмосфере развитой Agile культуры.

Не понимаю, откуда столько лайков статье.

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

Все верно!

Но автор делает акцент на том, что использовать обобщенный цикл разработки «Plan - Do - Check - Act», вы ни коим образом не сможете на горизонте 1 спринта (5 рабочих дней).

Концовка убила:

Можно ещё много чего сказать о том, что пошло не так со Scrum. Agile методологии сложны. Это набор принципов и ценностей, которые не связанны между собой логически. А значит всегда есть соблазн что-то выкинуть или изменить, ведь трудно понять, на что это повлияет. Когда-нибудь мы, как сообщество, решим эту проблему. Но не сегодня.

В мире давным давно уже все решили и без всяких проблем.

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

Обновления каждый час не означают, что над выкаченной фичей начали работать час назад. А итерация вроде как подразумевает цикл от начала до завершения работы.

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

Очевидные вроде как вещи. Вот гляньте индийский фильм, может быть придут светлые мысли :)

А чем Scrum отличается (ну кроме лексики) от утренней планерки у директора металлургического комбината (ну или любой другой конторы)? Я первый раз на такой "скрам" попал в 7 лет, когда садик уже закончился, а школа не началась. Целый месяц на таких скрамах сидел. По сути никакого отличия кроме "я вчера писал код для фичи А, а сегодня буду переписывать, так как продакт передумал" VS "я вчера ..ля как последний ...к ...ля работал над этими ...ми деталями к заказ-наряду, а сегодня утром выясняется, что они ...й не .... и вообще надо делать было другое"
Скрам это тупо маркетинговая обертка над банальной планеркой

В целом не отличается ничем, но, как обычно, есть ньюанс. Если вы не завод а, например, огромная ИТ корпорация, и вы пытаетесь выпустить нечто на рынок, то для многих (очень многих) людей оказывается откровением что:

  1. не нужно составлять планы "как я буду год строить платформу" тратя кучу времени и ресурсов на планирование чего-то что никогда не случится.

  2. Команда из 5-7 человек из разных отделов, закрепленная за проектом эффективнее чем "если тебе нужна виртуалка напиши заявку в отдел виртуализации и жди согласования всех менеджеров 2 недели", "если тебе нужен репозиторий заведи заявку и жди согласования 20 ИБшников целый месяц".

  3. У продукта должен быть цикл проверки состоятельности с реальным решением его дальнейшей судьбы не раз в 5 лет.

Лично я думаю что в целом scrum - естественный враг бюрократии. И в результате его превратили в непонятно что пытаясь продать максимально бюрократизированным кампаниям.

Тут еще тот нюанс, что слово "команда из 5-7 человек" не надо понимать слишком буквально: речь идет именно о том, что если для задачи надо всего 5 чел - то и надо взять команду в 5 чел чтобы они ее делали, а не растягивать по всем инстанциям.
Командой, не всем колхозом.

Но люди склонны абсолютизировать, и начинают "собирать команду", когда достаточно одного Васи Пупкина. "У нас же написано - команда должна быть!!!"

" "если тебе нужна виртуалка напиши заявку в отдел виртуализации и жди согласования всех менеджеров 2 недели", всё гораздо проще должно быть МенеджерСогласований.Согласовать(отделВиртуализации, списокМенеджеров);

Наверно, вы имели ввиду: чем отличается "Ежедневный Scrum" (он же stand-up встреча) от планёрки. Потому что Scrum - это методология организации деятельности, и это далеко не только stand-up'ы. По своей сути - не отличается. Stand-up это действительно планёрка (за исключением нюансов, связанных с PDCA-циклом).

Но в вашем комментарии я заметил боль: "На планёрке мы рассказываем о том, что будем переделывать, потому что заказчик передумал". Такие ситуации говорят о нездоровом рабочем процессе. И часто означают некомпетентность менеджеров: линейного (бригадира) и проектного (начальника цеха).

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

Если говорить про Scrum, то это решается "планированием спринта". Когда заказчик согласился с "критериями завершённости" спринта, тогда программисты идут работать, и план на спринт больше никто не изменяет. Приоритеты никто не трогает и люди спокойно работают в течении всего выделенного времени (а это, как и написано в статье, от 2 недель до 1 месяца)

Если у вас часто происходят такие ситуации со сменой приоритетов, то вашим менеджерам нужно больше компетенций. Ну или вам, чтобы им это объяснить.

200%. Но попробуй, сделай замечание такое менеджерам. Они сразу же обидятся и скажут тебе, что ты не гибкий... У работника нету власти, кроме как уйти

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

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

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

Недельные ещё хуже, в недельные только мелкие задачи помещаются и там приходится декомпозировать и всякие feature-флаги использовать, что тоже плохо в некоторых случаях, т.к. контекст меняется слишком часто. Вместо того чтобы 2 недели потратить на задачу целиком (с обсуждениями и аналитикой), 2 дня тратится на кусок, он скрывается, потом в следующий спринт опять 2-3 дня на кусочек, но надо восстановить контекст чё там в этой задаче, а самое плохое, это то что параллельно аналитики/заказчик/архитектор что-то передумали, т.к. времени на аналитику не выделено, а заказчик хочет фичу как можно раньше и даже этот уже написанный кусок можно стирать и писать заново.

И на самом деле фича формально берется в работу раньше (код пишется раньше), но получается либо хуже, либо дольше, либо и то и другое.

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

Эх, как же раздражают эти разработчики, которые хотят ТЗ до запятой, и чтобы код ушел сразу в прод и потом копировался из версии в версию. А то что для этого надо, чтобы на твой трехдневный код аналитик месяц пахал - их не волнует.
Дешевле же в человекочасах написать, показать и выкинуть 90%, потом написать оставшиеся 90% - и на это уйдет 2 спринта до прода, вместо трех только на аналитику.

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

Так только в фантастике бывает. Ну и у всех этих философов, которые книжки про методологии пишут.

Про нападки на ООП, даже писать лень, опять какие-то самопиарщики с дефицитом внимания, пытаются его на себя обратить.

Для этого есть “ретро”, где обсуждаются эти моменты. Вопросы частой смены приоритетов должны обсуждаться именно здесь.

Обсудили. И что это дает, кроме расхода времени и психологическую самопомощь?

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

Деятельность исполнителей должна иметь смысл.

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

А результат деятельности наемных работников им по-любому не принадлежит.

У вас интересный взгляд на деятельность. Неужели вы хотите прожить жизнь, занимаясь неудовлетворяющей вас деятельностью? И вы готовы с этим мириться, если вам за это немного платят?

Предлагаю мысленный эксперимент. Вы занимаетесь хобби? Если да, то вам за него платят? Если нет, то получается вы занимаетесь деятельностью, которая не приносит вам денег. Зачем?

Счастье для человека - когда он за деньги делает то, что делал бы и бесплатно. А для компании счастливый сотрудник - это надёжное вложение, потому что он будет эффективно работать в компании до самой пенсии. Чужое счастье деньгами не купишь. Подробности в книге "Практическая конфликтология" Виктора Пономаренко

Неужели вы хотите прожить жизнь, занимаясь неудовлетворяющей вас деятельностью? И вы готовы с этим мириться, если вам за это немного платят?

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

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

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

Не существует компаний, которые могли бы конвертировать, скажем, моё желание читать книжки, изучать теоркат и писать всякий код по фану (до уровня proof of concept, не доводя до продакшена), в деньги, которые они бы могли бы потом мне платить.

Я вас понимаю. Сам люблю творческий подход к своей деятельности. Что-нибудь изучить, усомниться в предположениях авторов, создать новую модель, поэкспериментировать.

Природа не создаёт ненужных людей. Я уверен: при должной рефлексии, вы, с владельцами компании, смогли бы найти деятельность, которая была бы как полезна для компании, так и удовлетворительна для вас.

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

Мы все разные и все в чём то хороши и полезны. Я бы не сдавался так просто

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

Природа не создаёт ненужных людей.

Это просто неверно. Прогресс идет через изменчивость и отбор, и никак иначе. Составить план прогресса на практике невозможно (ну, если не пользоваться хаками реальности типа божественного всеведения).

А потому изменчивость создает разные варианты, а отбор отбирает нужные.

Мы с вами смотрим на это в разных временных промежутках.

Вы применяете это утверждение к каждому конкретному человеку. То есть, если человек родился с отклонениями - то это противоречит тому, что "природа не создаёт ненужных людей".

Я смотрю на это с точки зрения всей эволюции человечества, сглаживая выборку. То есть, если много людей обладают одной и той же особенностью мышления / поведения / нервной системой, и эта черта всё ещё существует, значит она прошла отбор и, по каким то причинам, для человечества полезна.

Это мысль из разряда "Не оценивайте рыбу, по её способности лазать по деревьям"

Мы с вами смотрим на это в разных временных промежутках.

То есть, нам тут спорить не о чем.

Я согласен с вашей позицией. Если и вы согласны с моей, то мы оба правы. В таком случае да, спорить не о чем

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

Ну и советовать книжку какую-то прочитать в ответ на комент, типа «вот это прочитай - тогда поймешь о чем я» - такое себе..

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

Согласен почти со всем, кроме одного.

Почему позволили собачке подрасти?

Предположим, что мы делаем не информационную систему, а строим дом. Какой MVP можно дать заказчику после одного витка (спринта)? Если у заказчика выросли хотелки по тем этапам, которые уже прошли (не обои выбрать, пока ещё стены строим, а площадь застройки, планировку конфигурацию инженерных сетей поменять, когда уже крыша готова и обои наклеены) - принять поправки в бэклог? 😉

Так в этом и смысл Скрама - сделать подвал с гаражом и сдать их заказчику. Если вдруг окажется, что у того джип не пролезает в ворота, переделать ДО того, как начали строить этажи.

Вот только, редко кому нужен гараж без дома.

С этим бы я не спорил. Но именно в том и проблема (проверено на многих проектах, где я был, увы, не ПМ, который не допустит такого, а лишь аналитиком), что аппетит к заказчику приходит в процессе. И да, я сторонник водопада для крупных проектов. Именно проектов, а не процессов, и не штучной разработки с неизвестными требованиями к результату - там да, по Кенеди или Стейси лучше скрам.

Давным-давно, году в 2001м примерно, начальница давала мне почитать толстый том по управлению проектами. Точное название и авторов, естественно, не помню. Но помню, что там описывалась методика спиральной разработки. Мне кажется, для большого проекта она лучше и водопада, и скрама

Книга, возможно, эта: https://www.livelib.ru/book/1000022807-upravlenie-programmnymi-proektami-dostizhenie-optimalnogo-kachestva-pri-minimume-zatrat-cdrom-robert-t-fatrell

Что касается методик, то после прочтения этой книги начинаешь подозревать, что всё новое это хорошо забытое старое.

Как минимум, издание было другое (подарочное издание малым тиражом, коричневая обложка с золотыми буквами, год 2001, или скорее, 2000), авторов не помню...

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

Похоже, что его, хотя я такой фамилии не помню ) Сейчас понимаю, что уже и деталей самой методологии не помнил...

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

Нуууу. Недавно делал ремонт в квартире. Если бы все делали водопадом, я сейчас был бы намного менее доволен ) Понятно, что несущие стены не переносили, но, например:

1) одну межкомнатную перегородку наращивали на 50 см по сравнению с планом силами электрика-многостаночника и штукатура уже после отъезда каменщика

2) так же, уже после начала первичной прокладки электропроводки, пришлось входную дверь немного сдвинуть для организации электрощита в другом месте (сразу размеры щита неправильно посчитали)

2) розеток добавили в комнатах 4 штуки (сейчас понимаю, что надо было 6) уже после черновой штукатурки, 1 перенесли

3) робот пылесос за время ремонта получил возможность подключения к воде и канализации, поэтому, был перенесен из комнаты в прихожую (к стояку) с добавлением и там розетки + ответвления по сантехнике и соответствующая перепланировка мебели

А по поводу этажей, Вы не поверите, но есть в Москве примеры домов, когда на хрущевские 5 этажей наращивали сверху еще 4!!! https://vm.ru/moscow/206479-hrushevke-narastili-etazhej-unikalnaya-strojka-pozvolit-uvelichit-imeyushiesya-v-starom-dome-kvartiry-i-poluchit-novye

А я, например, в новой квартире делал ремонт водопадом, со строгим контролем соответствия чертежу и 3d модели, сделанным дизайнером на отдельном этапе проектирования. И остался очень доволен.

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

А все вещи по проводке спокойно решались на этапе согласования чертежей.

Со щитом получилось недопонимание между мной и электриком на фоне моего желания иметь фишки умного дома и нашего обоюдного на тот момент незнания, как этот умный дом можно реализовать + до меня дошло, что в первоначальном варианте щит будет торчать в прихожей на виду (я почему-то думал, что его полностью "утопят" в стену).

А это уже поисковая работа, НИР (НИОКР) 😎

Вот тут только гибкие методологии и работают

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

Требования меняются потому, что:

  1. Изначально требований никто не знает (вопрос к аналитикам...)

  2. Руководство (ПМ, руководители компании) допускают вариативность требований, значительно меняющих длительность и ресурсоëмкость работы.

Вот только во втором случае это уже не проект, если следовать определению понятия "проект" 🤷‍♂️

Этим SOFTware и отличается от HARDware. Железки сначала разрабатываются, а потом пускаются в производство. Больше вы не можете их изменить. А вот программы вы можете изменить всегда.

Создание архитектуры дома делается по Agile. А вот строительство по CCPM. Так что сравнение с постройкой дома некорректно. Это принципиально разные типы проектов

Железку нельзя изменить, когда она уже изготовлена. Хотя как сказать... Можно "тюнинговать" (что мы часто видим на примере автомобилей). А выпустить новую по обновлённым чертежам и техкартам можно легко.

С софтом почти так. Если говорить о релизах - то они сравнимы с железками. Только несколько проще софт переделать, чем железку. Но, если по-честному, новый релиз софта - это, де-факто, новая "железка".

НО! Всё зависит от того, до какой степени изменять и железку, и софт (и дом).

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

Кто мешает снять с компьютера один компонент и заменить на другой (хард сменить на SSD, или заменить видеокарту на совместимую, заменить планку памяти и т.п.), НЕ МЕНЯЯ архитектуру?

Кто мешает в доме заменить обои, розетки (не перенося их), люстру?

А вот если задача КАРДИНАЛЬНО переделать - то тут и с железом, и с софтом проблемы будут.

Пример. Была сделана для одного крупного общероссийского межрегионального проекта СХД, в которой данные ежедневно трансформировались в витрину данных, дополняя "исторические" данные оперативными прошедшего дня. Всё было рассчитано так изначально (и протестировано на "фыве"), что за время ночи (от закрытия дня в Калининграде до начала рабочего дня в Петропавловске-Камчатском) данные должны успеть преобразоваться. Работа на реальных данных показало, что "не сезон". Пришлось переделывать. Полностью. Модель данных. После того, как система пошла в опытную эксплуатацию.

Причём требования "в процессе пути" не менялись, т.е. "собачка не могла подрасти". И НИЧТО не мешало аналитикам и архитекторам СНАЧАЛА подумать, потом делать.

Резюме. Да здравствует водопад там, где он должен здравствовать!

Lol, как человек, работающий на металлургическом комбинате, подтверждаю

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

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

Идеи эволюционируют, адаптируясь под условия. Ну не может ни скрам, ни ООП оставаться тем же, что и 30-40 лет назад, когда мир меняется.

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

Но в целом и ООП, и скрам - хорошие идеи для разработки (если не разбивать о них лоб).

Если не разбивать о них лоб - это как раз про то, что они потеряли смысл.

Идеи не меняются. Их бывает толкуют не верно. Но скорее появится что-то новое, чем старое изменит смысл.

Скрам - не скрам, если его изменить. Но люди в скудоумии своём не могут в абстракции и дать другой идее новое имя. По этому как те зомби повторяют - Мозги.

Скрам - не скрам, если его изменить.

Это всего лишь защита от претензий к авторам скрама, ибо все мы знаем, как вольно люди способны выполнять инструкции, а потом предъявлять претензии за их некорректность. Сами авторы сделали около 6 ревизий скрама с 2010 по 2020 год, а исходные идеи зародились ещё в 1980х.

Так какого рода изменения были сделаны авторами?

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

Но никто не убирал какие-то митинги или менял их цель.

Ну вот список изменений 2010-2020гг. на 30+ пунктов: https://scrumguides.org/revisions.html Если уж авторы решили их добавить, значит они были критичны.

В первой ревизии строго (ведь отклонение - это не скрам) предписывалось делить участников на поросят и цыплят. Это делало ритуалы в те времена максимально странными. Хорошо, что от этого отошли.

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

Менять описание для понятности это не тоже что менять суть.

Идея, тем не менее, в сути своей не изменилась.

Формулировка идеи изменялась много раз, это как раз показатель того, что идея уточнялась. В первой версии предписывалось разделять участников на поросят и цыплят, во второй - вести burndown диаграммы, а в последней - и то, и другое стало необязательным. Да, можно было попытаться выделить "суть" и отбросить "лишние ритуалы", но ведь указывается, что это будет не скрам.

Поэтому, на мой взгляд, логично в случаях, где скрам подходит, брать скрам точно в том виде, как он описан в руководстве 2020 года, и называть его "Scrum v2020", а не пытаться выделить "суть", имплементировать некую её интерпретацию, и спорить с другими скрам-мастерами, что есть скрам, а что не есть. То есть в данном подходе идея заключается во всей формулировке, а не в её части.

Но ни первое, ни второе не отменило ни одного изначального "ритуала". Т.е. скрам остался скрамом.

Про версионность поддержиыаю 100%. Так понятнее будет всем. Вдруг кто знаком со старой версией.

Это не они потеряли свой смысл. Это люди потеряли их смысл.

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

И это прекрасно. Даже в изначально FP языки добавляют объектную нотацию.

Но вот сами программисты могут и не пользоваться мульти-парадигменностью. Если почитать книги из 90-ыx / 00-ых (например "Применение UML 2.0 и шаблонов проектирования "), то там советуют ВСЮ программу писать в ООП. Да не просто писать, а ещё и проводить анализ и проектирование в ООП терминах.

Учитывая объем и кол-во материалов, которые предлагают так делать, напомнить о мульти-парадигменности (и областях применения разных парадигм) лишним никогда не будет

НЛО прилетело и опубликовало эту надпись здесь

Да книги-то как обычно разные были, кто что читал. А так например как раз в 98-м вышла коплиеновская Multi-paradigm Design for C++

А что в этом прекрасного?

Прекрасно в этом то, что программисты могут использовать и используют подходящие инструменты для своих задач. Не нужно пытаться скрывать данные в объектах, когда они нужны в UseCase'е - используйте FP. Не надо разделять сервис на 10 функций и замыкать в каждой из них аутентификационные данные для этого сервиса - используйте OOP

Здесь возникает по крайней мере два вопроса:

1. Откуда вообще следует это аналогия между (мне не очень нравится термин, но назовём это условно) парадигмой и инструментом?

2. Допустим даже, что есть смысл выбирать парадигму под задачу. Как из этого следует, что смешивать разные парадигмы в одном языке, усложняя его — это хорошая идея?

> Как из этого следует, что смешивать разные парадигмы в одном языке, усложняя его — это хорошая идея?

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

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

В идеальном мире всё пишется на DSL. Но в реальности мы вынуждены идти на компромисс. С одной стороны у нас несколько парадигм, которые мы применяем в разных частях приложения. Да, их 3 штуки, и это по определению сложно. С другой стороны мы можем выбрать одну парадигму, но тогда некоторые части приложения будут невыразительными.

Пока этот компромисс не будет решён - мы будем занимать какую-то сторону (1 парадигма vs 3 парадигмы). И я и вы правы, потому что мы оба указываем на 2 необходимых условия: выразительность и простота. Когда IT-индустрия дорастёт до DSL - оба этих условия будут соблюдены и все будут довольны

Хорошая статья. Очень показательно, что люди не понимая идею, выстраивают карго-культ. Наконец-то кто-то попытался донести простую истину: "Не сотвори себе кумира"!

Со скрамом так получилось изначально, так-как стали делать бизнес на сертификации. Чтобы стать кумиром, надо было заплатить. Вот и расплодилось скрам-мастеров.

Скам - мастера

"Критикует - предлагай"

Сервис/канбан, да любой из agile, просто набор церемоний , как конструктор, собери под свой проект, кажется agile как раз про гибкость, а не твердолобое выполнение церемоний, agile manifesto вам в руки.

Тоже самое про ООП.. в ООП яп давно пышные вещи.

Попробуйте разработать что-то большое без всего этого, покажите что получилось. Новички часто страдают критикой, вырабатывают какие-то мега инструкции, пробуют какие о новые подходы без адаптации к задаче и говорят : ну гавно же. Не гавно, просто выбирайте инструмент под задачу, и даже выбранные инструменты дорабатывайте под задачу. Как раз делал ревью кода, где чувак не понимает , что речь это не rmi, что нужно немного в структурность, не нужно писать дубликаты типа getbycompanyid, getbyclientid, getbyxxxx, не понимает зачем и как валидироватьтвходные данные, но твердо предлагает перейти на другой подход к бранчингу в проекте.

Тоже самое про ООП.. в ООП яп давно пышные вещи.

Попробуйте разработать что-то большое без всего этого, покажите что получилось.

Раскройте пожалуйста тему, почему без ООП, по Вашему мнению, нельзя построить что-то большое?

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

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

А по моему опыту все большие проекты написаны процедурно, с использованием атрибутов ООП только по синтаксической необходимости. Причём это происходит не потому, что так кто-то специально придумал, а потому, что это самый простой способ писать говнокод, к которому с неизбежностью всё скатывается через год-другой. Через 10 лет – к собственным особым версиям компиляторов, необходимых для построения проекта, а через 30 лет – к заплаткам в машинном коде (к этому времени исходные тексты обычно в существенной части уже утеряны).

Даже линукс, который на С и тот использует ООП-приёмы, когда в структурах пишутся колбеки на функции для этой структуры.

Вам не странно "колбеки на функции" называть "ООП-приёмом", хотя даже сами термины относятся к другим идеологиям?

Кроме того, линукс как целое – чисто процедурный программный комплекс, ни на что другое не претендующий. Огромный набор исполняемых модулей (процедур), слепленных в одно жалкое целое процедурными скриптами.

Вам не странно "колбеки на функции" называть "ООП-приёмом", хотя даже сами термины относятся к другим идеологиям?

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

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

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

Это больше похоже не на ответ а на рекламный слоган.

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

Не совсем, его основная парадигма связывание состояние и поведения с сокрытием первого. Скрывать детали реализации и без ООП все умеют.

Даже линукс, который на С и тот использует ООП-приёмы, когда в структурах пишутся колбеки на функции для этой структуры.

Это называется натягивание совы на глобус.

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

Хорошо, давайте рассуждать. Сразу выскажусь, солидарен с @vadimr, что большинство больших проектов, по которые мере которые я видел, написаны процедурно, в лучшем случае с элементами ООП и ФП.

А теперь давайте аргументирую почему так.

Frontend приложения
можно не комментировать, там давно ушли от ООП в сторону ФП в большинстве проектов и решений

Backend приложения
В большинстве приложений используется подход anemic model который скорей опосредованное отношение имеет к ООП так как при нем данные и функции разделены, вся суть такого подхода - сделать из объектов-сервисов помойки с методами и зависимостями без состояния и обмазать это паттернами и костылями избегая проблем данного подхода. Видимо, работать с данными связанными с поведением исторически оказалось не очень удобно.
Далее, реляционные базы данных не очень дружат с ООП представлением данных и приходиться связывать их через костыли в виде ORM со своими минусами и проблемами. Как только приложение разрастается и требуется производительность, все ООП убирается в дальний ящик и начинается процесс написания старого доброго SQL с маппингом в "тупые" объекты.
Чем больше растет приложение - тем обычно сложнее поддерживать граф связей между объектами и тем чаще на это забивают.

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

Языки программирования и подходы
Не трудно заметить что за последние годы все больше и больше в мейнстримные языки программирования добавляют функциональности из ФП: дженерики , алгебраические типы, ФП подобные подходы с обработкой данных конвейерным образом, монады, все популярнее подход при работе с чистыми функциями и неизменяемыми данными. Каким ООПшные возможности добавили за последние годы?
Более того, такие современные языки программирования как Rust и Go вообще не строяться вокруг ООП парадигмы, имея скорей некоторый удобный синтаксический сахар чем полноценную поддержку.

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

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

Я не отрицаю, что бывают проблемы быстродействия, которые надо решать. Только они, как правило, затрагивают ну никак не больше 1% кода. Я так, навскидку. Бывает, что 0,1% кода выполняется 99% времени. Про микросервисы, где 80% времени и цпу уходит на сериализацию и ио, я не говорю. Ни к какому чёрту ООП не идёт. Как правило оптимизируется небольшая часть кода, которая тормозит. Без выхода из парадигмы. Напомню, что большинство игр написано на C++. Возможно, с привлечением низкоуровневых кусков сей или там ассемблера, но не факт.

Сюда же зачем-то примешали ОРМ, который тоже, блин, инструмент. И тоже для снижения когнитивной сложности проекта. И тоже в подавляющем большинстве случаев не является бутылочным горлышком для быстродействия.

ООП даёт инструменты абстракции. Без которых не будет работать DDD и анемичная модель тоже. Кстати, про то, что в большинстве больших проектов анемичная модель - это очень громкое заявление.

О том, что мейнстримовые языки становятся мультипарадигменными, я спорить не буду. Только по прежнему в проде я не вижу больших проектов на чисто фп, вроде F#/scala/haskell.

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

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

Напомню, что большинство игр написано на C++

А большинство сайтов написано на PHP, что дальше?

Сюда же зачем-то примешали ОРМ, который тоже, блин, инструмент. И тоже для снижения когнитивной сложности проекта.

В каком месте? ОРМ это костыль для связывания реляционного и объектного представления. Снижение когнитивной нагрузки - это когда у меня есть БД, я с БД работаю как с БД плоско и просто. Когда мы жонглируем маппингом с реляционной модели к объектной туда сюда и делаем вид что то БД есть, то БД нет, то снова БД есть, это точно не снижение когнитивной нагрузки.

ООП даёт инструменты абстракции. Без которых не будет работать DDD и анемичная модель тоже.

За деревьями леса не видно? Преимущество парадигмы в том что без нее не будет работать подходы основанные на этой парадигме? Мне казалось мы проблемы должны решать а не оправдывать подходы ради подходов.

О том, что мейнстримовые языки становятся мультипарадигменными, я спорить не буду. Только по прежнему в проде я не вижу больших проектов на чисто фп, вроде F#/scala/haskell.

Отличный вопрос. Да действительно, на чистых ФП языках больших проектов сильно и сильно меньше, это факт. Но позвольте задам встречный вопрос - сколько больших проектов на чисто ооп языках?
Разуметься, языков без статических методов и обычных функций,
где все - объект, где нет дженериков, лямбда-функций, где нет АТД и т.д., где вся программа это чистое взаимодействие объект-объект.
Много таких проектов вспомните?

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

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

Собственно, я эту проблему и решаю. Для себя. И для меня хорошо себя зарекомендовали инструменты ООП, которыми я умею пользоваться и они мне приносят пользу.

ОРМ это костыль

Не костыль, а инструмент. Для связывания, да. Очень много разрабов считают, что в ООП работать проще, чем с плоскими данными. И я к ним тоже отношусь. И если вы предпочитаете кидаться чистым sql и выгребать каждый раз свой набор полей, то у меня в ORM синтаксис намного короче и я оперирую объектами, а не полями, что уже на одну абстракцию выше. Кроме того, у меня есть возможность одним кликом узнать, в каких частях проекта используется вот это поле.

За деревьями леса не видно?

А давайте нормально разговаривать? Без обвинений в недальновидности

Преимущество парадигмы в том что без нее не будет работать подходы основанные на этой парадигме?

А кто мне только что ставил в упрёк, что современные системы - это анемичная модель, а не ООП?

Проблема большого проекта - в том, что он большой. Его тупо трудно удержать в голове и всё сложнее править.

Нет, сам по себе размер это не проблема. У вас может быть backend в котором тысяча ресурсов и все операции для них crud. Никаких проблем и даже надобности все это в голове держать нет. Другое дело сложный и запутанный проект может быть и небольшим но очень проблемным.

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

Не костыль, а инструмент. Для связывания, да. 

Инструмент для решения проблемы которую мы сами себе и придумали.

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

Звучит как концентрация на решении вместо проблемы.

А давайте нормально разговаривать? Без обвинений в недальновидности

Я Вас ни в чем не обвиняю а указал на то что странно когда преимущество парадигмы в том что они служит базой для подходов на ее основе а не в том что она решает конкретные проблемы.

У вас может быть backend в котором тысяча ресурсов и все операции для них crud.

Не может.

Решений проблемы сложных проектов масса, все они в основном сводятся к правильной модульности.

Вуаля. ООП как раз и предлагает модульность. На уровне классов.

ООП не является волшебной таблеткой от этой проблемы

Как ничего не является волшебной таблеткой.

Инструмент для решения проблемы которую мы сами себе и придумали.

Базы данных мы тоже сами себе придумали.

Звучит как концентрация на решении вместо проблемы.

Это как вообще?

Не может.

Физически не может или юридически или не может потому что не может быть никогда?

Вуаля. ООП как раз и предлагает модульность. На уровне классов.

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

Базы данных мы тоже сами себе придумали.

Базы данных хранят данные. ORM выполняет работу по перекладыванию данных потому что мы решили что так идеологически правильно.

Это как вообще?

Вы рассуждаете в контексте инструментов а не проблем. Когда Вы говорите что многие как и Вы считают что ООП удобнее и что ORM удобнее Вы забываете упомянуть для чего. Допустим у нас система отчеты строит и статистику собирает, путем выполнения сложных запросов, нам точно нужна ORM и точно нужно придумать объекты реального мира для этой задачи или мы просто можем получить данные и передать их? Или вот у вас высоконагруженная система и очень бы хотелось чтобы она работала быстро но вот незадача, в нашей экосистеме приходиться выбирать: или синхронный ORM или асинхронный доступ без ORM. Вы предпочтете удобство производительности или все-таки исходя из задачи переступите через себя и выберете подходящий инструмент?

Физически не может или юридически или не может потому что не может быть никогда?

Оно не нужно на практике. Если у вас в проекте тысячи крудов и ни одной логики - вы в дурке. Бегите.

Как уже писал, умные объекты давно вышли из моды.

Анемичная модель, это, вообще, не про ООП, более-менее то же самое можно на Си (без плюсов) со структурами реализовать. Подозреваю, что ее и придумали сишники, которых насильно пересадили на Джаву

Тысячи круд ресурсов в системе не связанных между собой? Это не сложная, а просто объемная система. Даже сложно представить, для чего она может понадобиться?

Не просто так в SQL появились транзакции, люди давно поняли, что простым крудом сложную логику не реализуешь )

Тысячи круд ресурсов в системе не связанных между собой? Это не сложная, а просто объемная система. Даже сложно представить, для чего она может понадобиться?

Просто абстрактный пример для демонстрации что размер не синоним слову сложность

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

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

ООП - не более чем инструмент для резкого снижения сложности. Бьёрн Страуструпп именно так и объяснял ООП. Код и данные сильно связаны, поэтому мы их держим вместе, скрывая ненужные детали внутри. Модули, библиотеки - тоже снижают сложность.

Обмен сообщениями тоже инструмент снижения сложности, но из другой паралигмы, не ООП.

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

I made up the term 'object-oriented', and I can tell you I didn't have C++ in mind.
-- Alan Kay, OOPSLA '97.

Неа, именно из изначального ООП.

Судя по тому, как выглядит ООП сейчас, на языках C++, Java, C# и им подобных, "изначальный ООП" оказался не нужен - не так как Скрипач (который, напрминаю, гравицаппу стырил для компании внезапно), а реально не нужен. Не прошел отбор - но оставил хорошее, годное потомство.

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

Компиляторы ФП-языков написаны на ФП-языках, как минимум, а это достаточно простые проекты. Тот же ghc, например.

Заодно отвечу на соседний комментарий:

Только по прежнему в проде я не вижу больших проектов на чисто фп, вроде F#/scala/haskell.

Регулярно видел большие проекты на хаскеле в проде. И сам делал лично в одну морду, и на последней работе, например, очередной компилятор/тайпчекер на хаскеле был написан, и мы даже начали его формально верифицировать на coq (тоже ФП).

Просто есть тут такой один нюанс… например, был у меня один проект на хаскеле, пять тыщ строк, написан в одиночку по большому счёту. Если бы я его переписал на C++, то там было бы по моим оценкам примерно на порядок больше. Считать ли это «большим» [для одного разработчика] проектом? 50 тыщ строк для одного разраба — это уже средне-большое, как по мне.

Компиляторы ФП-языков написаны на ФП-языках,

Это хорошо, когда разработчики придерживаются старого правила "писать компилятор на компилируемом языке". Это как старая добрая традиция ставить архитектора под мост во время приёмки. Только это скорее необходимый минимум, proof-of-concept, чем пример реального проекта.

например, был у меня один проект на хаскеле, пять тыщ строк, написан в одиночку по большому счёту. Если бы я его переписал на C++, то там было бы по моим оценкам примерно на порядок больше

И что, это хорошо? Я могу сказать, в каком случае это хорошо. Когда в одном языке меньше бойлерплейт конструкций, чем в другом. А когда количество строчек сокращается за счёт повышения когнитивного порога восприятия кода - это плохо. Очень плохо.

Считать ли это «большим» [для одного разработчика] проектом? 50 тыщ строк

Во-первых это совершенно условные 50 тыс строк, которые вы вывели исходя из придуманного коэффициента. Вот передо мной проект ormfactory, который я пишу в одно лицо. Там 36kloc самого аппа, 6kloc тестов, 5 сайта, и ещё оракловый коннектор с бриджем ещё 5к. Питоногенераторы, батнички и доки я даже не считаю. Там ещё тулзы вспомогательные есть. 50 должно набраться. А это C#, который мультипарадигменный, и функциональщину там я не стесняюсь использовать, так что коэффициент 10 тут будет точно мимо.

Когда я писал erp-систему в течении 12 лет (не один), то там набралось более 200kloc. Вот это средне-большой проект. 50 - средний. А 5к - маленький, как ни крути.

Только это скорее необходимый минимум, proof-of-concept, чем пример реального проекта.

Это уже какие-то апостериорные фильтры по непонятным критериям.

Proof-of-concept — это компилятор идриса на идрисе (потому что он не умеет ничего, по большому счёту, и потому что ему несколько лет, и пилится он автором языка и полутора энтузиастами). А ghc — это проект, которому уже третий десяток лет, в котором под миллион строчек кода, весьма нетривиальный фронтенд/тайпчекер, очень нетривиальный пайплайн оптимизации, и в котором регулярно допиливаются новые (и часто нетривиальные с точки зрения реализации) фичи языка. В моих определениях это каноничный «большой проект».

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

Бойлерплейта тоже меньше. Компилятор много чего сам выводит, от, не знаю, функций хеширования для нетривиальных типов до беготни по AST целевого языка (мой проект был его компилятором).

А когда количество строчек сокращается за счёт повышения когнитивного порога восприятия кода - это плохо. Очень плохо.

Что лучше,

int sum = 0;
for (int i = 0; i < vec.size(); ++i)
  sum += vec[i];

или

const int sum = accumulate(vec, 0);

?

Есть разница между «когнитивным порогом восприятия кода при условии знания идиом языка, которые где-то примерно O(1) с точки зрения сложности изучения от числа проектов» и «когнитивным порогом восприятия кода потому, что код захламлен неважными деталями реализации с неверного уровня абстракции».

И по моему опыту, чем язык чище (в смысле, чем типы у него выразительнее), тем код читать проще. Код на идрисе или агде читать проще, чем на хаскеле, который, в свою очередь, читать проще, чем на окамле, который, в свою очередь, читать проще, чем на плюсах.

Во-первых это совершенно условные 50 тыс строк, которые вы вывели исходя из придуманного коэффициента.

Это основывается на опыте сравнения подобных проектов. Могло бы быть 40 тыщ, могло бы быть 60 — на суть это не влияет на этих масштабах.

50 - средний. А 5к - маленький, как ни крути.

На C++ — маленький. На хаскеле — средний. Потому что предметной сложности в нём будет как в проекте на 50к на плюсах.

Что лучше,

В шарпе я постоянно пользуюсь такими вещами, как Sum(i => vec[i]), Select/SelectMany, цепочки функций и другие элементы ФП. Так что в данном случае шарп с хаскелем будет 1:1. А вот специфичные для шарпа ситуации могут быть совсем не в пользу хаскеля. C# - очень насыщенный язык с огромным количеством синтаксического сахара, с ним мало кто может тягаться по выразительности и краткости. Не только лишь все это могут.

На C++ — маленький. На хаскеле — средний.

Мне это напоминает заявление представителя эппла, что их 8гб это как 16гб на "обычных" компьютерах. Настолько же голословное. У крестов есть мощная система шаблонов и если по шаблонам упороться, можно оставить любой фп язык позади без вариантов вообще. Я имею в виду по плотности смысла на строку кода.

В шарпе
C#

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

В шарпе я постоянно пользуюсь такими вещами, как Sum(i => vec[i]), Select/SelectMany, цепочки функций и другие элементы ФП.

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

Так что в данном случае шарп с хаскелем будет 1:1.

Окей, моё любимое упражнение. У вас есть дерево с интами и функция, которая по инту-айдишнику загружает по сети, скажем, комментарий с этим айди (ну или возвращает ошибку). Можете написать код на шарпе, который проходит по дереву, загружает эти комментарии, и либо собирает все ошибки (если была хотя бы одна), либо отдаёт дерево комментариев?

На хаскеле данное по условию выглядит как

data Tree a = Node
  { value :: a
  , children :: [Tree a]
  }

loadComment :: Int -> IO (Either NetworkError Comment)

— на C# переведите по вашему усмотрению.

У крестов есть мощная система шаблонов и если по шаблонам упороться, можно оставить любой фп язык позади без вариантов вообще.

Я по шаблонам упарываюсь профессионально за деньги. Так вот, это — унылое костыльное подобие, в зависимости от конкретного применения, одного из:

  • нормального параметрического полиморфизма (есть в любом современном ФП-языке из ML-семейства)

  • метапрограммирования а-ля haskell generics или template haskell

  • попыток считать что-то на типах а-ля type families или зависимые типы в идрисе.

(и именно потому, что это костыльное подобие, за знание шаблонов C++ готовы платить сильно больше — правда, ощущаешь себя при этом экзотической проституткой с широким спектром услуг, не более)

Учитывая особенности плюсов, весь код с темплейтами должен быть в хедерах (если упрощать, опять же — частные случаи явного инстанциирования мы не обсуждаем), что делает их использование для DI или для гарантирования инвариантов запретительным для реальных проектов. Ну или, если очень надо, то одно TU компилируется по несколько минут (что действительно больше, чем ФП-языки, но не по тем метрикам, по которым хотелось бы).

Тайпклассы + type families + GADT'ы оставляют шаблоны далеко позади.

Окей, моё любимое упражнение. У вас есть дерево с интами и функция, которая по инту-айдишнику загружает по сети, скажем, комментарий с этим айди (ну или возвращает ошибку). Можете написать код на шарпе, который проходит по дереву, загружает эти комментарии, и либо собирает все ошибки (если была хотя бы одна), либо отдаёт дерево комментариев?

Оу. У нас хвостовая рекурсия и discrimination unit, судя по всему. Ну чтож, давайте сравнивать шарп с хаскелем на поле хаскеля. Я точно также сделаю, выборку айдишников отдельно, запрос отдельно.

Можно и одной строкой развернуть дерево. Вроде этого:

IEnumerable<int> AllIds(Node root) => 
    [root.Id].Concat(root.Children.SelectMany(AllIds));

Только я сделаю рекурсивную проперть в ноде вроде такой:

IEnumerable<int> AllChildIds => Children.SelectMany(c => c.AllChildNodes).Concat([id])

Просто чтобы можно было с любого уровня разобрать дерево.

Потом запрос:

var comments = context.Comments.Where(
  c => node.AllChildIds.Contains(c.id))

Может чуть-чуть больше букв. Но в шарпе даже меньше строк. И для меня синтаксис хаскеля выглядит типичной ФП-сплющенной чёрной магией, где за смысл отвечают знаки препинания, а не слова. Это как раз то, что я не люблю в ФП. То, что мне не понравилось в котлине. То, за что я ненавижу регулярки. Оно на мой вкус не читается вообще. В шарпе - читается. В хаскеле - нет.

И да, я специально не стал отлавливать ошибки. Я знаю, что такое DU. Но ошибки надо понимать, где могут возникнуть. У меня запрос комментов идёт одним селектом в "IN" синтаксисе, он либо весь сработает, либо вообще не сработает. В дереве ошибок быть не может.

И да, я специально не стал отлавливать ошибки.

Выкинуть половину условия задачи и говорить, что сделали это специально — довольно странный флекс, но окей.

Но ошибки надо понимать, где могут возникнуть.

Они могут возникнуть при сетевом запросе. Где вы этот запрос делаете, кстати — не видно.

У меня запрос комментов идёт одним селектом в "IN" синтаксисе

Нельзя так. Вам API'шку дали, которая не умеет в запрос всех комментариев сразу, надо долбить по одному.

[...] Потом запрос:

Итого ошибки вы не обрабатываете, древообразную структуру комментариев потеряли, и, судя по форме запроса, потеряли даже порядок комментариев.

Можете всё-таки написать одну функцию целиком, которая это всё делает?

И при всём этом

Но в шарпе даже меньше строк.

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

И для меня синтаксис хаскеля выглядит типичной ФП-сплющенной чёрной магией, где за смысл отвечают знаки препинания, а не слова.

Конечно, куда лучше, когда знаки препинания не отвечают вообще ни за что, зато(везде(понатыканы<(разные<скобочки>)>)), которые не несут вообще никакой смысловой нагрузки и являются просто визуальным шумом. Да и IEnumerable<int> читать куда проще, чем [Int]! Правда, создавать одноэлементный список всё равно приходится через [], но неважно.

Кстати,

У нас хвостовая рекурсия и discrimination unit, судя по всему.

хвостовой рекурсии здесь нет (и при обходе дерева быть не может, кажется, но мне лень это доказывать), а discriminated union из одного конструктора — это просто класс/структура, если в ваших терминах.

Выкинуть половину условия задачи и говорить, что сделали это специально

У вас условие притянуто за уши к функциональщине. Ошибки так не ловятся. Сове больно и обидно. Вы хотите что-то поймать, но сами не знаете, что и зачем.

Если надо задачку решить шарпом - не сомневайтесь, я её смогу решить. Надо насобирать либо коммент, либо почему айди коммента нет? Да не вопрос. Ещё пара строк. Всё равно меньше по строкам будет, чем у вас.

Где вы этот запрос делаете, кстати — не видно.

А давайте теперь поговорим о том, что у вас тоже нихрена не видно. Ни куда ошибки уходят, ни как они парсятся, ни как идёт запрос в базе. Тоже очень дохрена условностей.

У меня стандартный апи к Entity Framework, если что. Довольно узнаваемый.

Итого ошибки вы не обрабатываете, древообразную структуру комментариев потеряли, и, судя по форме запроса, потеряли даже порядок комментариев.

Можете всё-таки написать одну функцию целиком, которая это всё делает?

Ничего подобного. Структура на месте, ошибки наверху ловятся. Функция ради функции - ну определитесь с границами. Я не пойму, чего вы хотите. Доказать, что хаскель компактнее шарпа? Ну это, мягко говоря не так.

И я вам скажу, что ваш код нихрена не делает. Вы не разворачиваете дерево, вы не парсите ошибки, вы не запрашиваете комменты. А почему? Потому что я обвязки этого кода не вижу.

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

Код делает больше, чем вы просили. Просто вы не можете его вставить в свою любимую парадигму. Он там работать не будет.

У вас условие притянуто за уши к функциональщине.

У меня условие притянуто за уши к реальным задачам, которые приходится решать.

Причём это не только работа с тупыми апишками. Это, например, и backtracking-решение систем констрейнтов, но задачу в терминах апишек поймёт больше людей.

Ошибки так не ловятся. Сове больно и обидно. Вы хотите что-то поймать, но сами не знаете, что и зачем.

Почему не знаю? Знаю! Вон в типе функции написано, что она может дать NetworkError. А у вас в типе функции что написано? А ничего не написано, потому что и функции нет, есть какие-то обрывки кода, демонстрирующие LINQ.

Если надо задачку решить шарпом - не сомневайтесь, я её смогу решить.

Ну так решите уже, наконец, тем более, что по вашим словам это очень просто! Ещё раз на всякий: вам дан тип деревьев из стандартной библиотеки, и у вас есть функция загрузки одного комментария (её Вася написал, не вы), на C# будет выглядеть как-то так, наверное (не знаю, что у вас там вместо Either принято):

Either<NetworkError, Comment> LoadComment(int id);

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

Всё равно меньше по строкам будет, чем у вас.

У меня будет одна строка на тип функции и одна-две строки на реализацию. Всё. У вас уже больше.

Структура на месте, ошибки наверху ловятся.

На каком верху? Где у вас все ошибки собираются в случае, если появляется хотя бы одна? Где это поведение видно в типах?

И я вам скажу, что ваш код нихрена не делает.

Использование в будущем времени слов «код, который я вам покажу» вам ни на что не намекает? Мой код — это условие задачи. Что дано. И парсинг ошибок, например, происходит в функции загрузки комментария.

Я просто не хочу портить своей непонятной функциональщиной ваш чистый сишарп — покажите ваше решение вы, и я потом покажу своё.

Ну так решите уже, наконец, тем более, что по вашим словам это очень просто!

А нахрена же вы комментарии по одному грузите? За это даже на собеседованиях на джуна руки отрывают.

Вы ведь не понимаете, что такое модельная задача, да?

Я уже написал выше, нахрена. Например:

Вам API'шку дали, которая не умеет в запрос всех комментариев сразу, надо долбить по одному.

— и да, такие случаи бывали на практике. Сервис, реализующий апишку, чужой, а вы работаете в крупной копрорации, где добавление батчинга в чужой сервис займёт пару месяцев, а фичу неплохо бы деливерить поскорее. «$lang применим только к академически чистым ситуациям» шутят обычно как раз при $lang ≔ haskell, и получается смешно (метасмешно?), когда таким языком, похоже, оказывается C#.

Или, например:

Это, например, и backtracking-решение систем констрейнтов

где аналога «грузить все комменты сразу» просто нет.

Но вы уже третий комментарий уворачиваетесь от того, чтобы просто написать код на выразительном сишарпе, что, похоже, должно занять у вас меньше пяти минут (а на обсуждение, почему этого делать не надо, вы уже точно потратили больше). Кажется, что-то здесь нечисто!

Мы с вами не сработаемся. Спасибо за уделённое время, можете не перезванивать.

Если серьёзно, то да, бывает что нужно так. Только вы забываете про одну мааааленькую деталь. Когда я работаю над проектом, я работаю на шарпе. И у меня нет деревьев хаскеля. У меня всё другое. И просить сделать c# метод по хаскель дереву - в высшей степени некорректно.

Давайте структуру шарпа, я сделаю.

А как вы там древообразные структуры представляете, что у вас там в стандартной библиотеке для этого есть? (ведь есть же? выразительный же язык, удобный?)

Ну вот так и представьте. У вас полная свобода, делайте как вам удобнее. Только, ну, чтобы дерево, конечно. Я не знаю C# достаточно хорошо (и вообще только читать на нём могу, считайте), чтобы вас ограничивать написанием структуры за вас.

На плюсах я бы предположил, что где-то ещё написано что-то вроде

template<typename V>
struct Tree
{
  V value;
  std::vector<Tree<V>> children;
};

а как там на шарпе — я хз.

Нет-нет. Шарпа. Мне же надо на шарпе сделать.

И скажите, какой апи для получения комментов. И для вывода ошибок. Что это будет, вьюмодель, джсон или ещё что?

Если это какой-то бекенд, то давайте сначала ПОЛНЫЙ текст на хаскеле обработки апи. Вместе с загрузкой дерева, вместе с отправкой комментов. Вместе с сериализацией. А потом будем сравнивать именно полный текст.

У вас полная свобода, делайте как вам удобнее.

Я так и сделал. В чём проблема? Вы даже не потрудились сказать, где может ошибка возникнуть.

Всё ваше недовольство - это то, что сделано не по-вашему. Так по вашему и не надо. Надо, чтобы работало.

У меня есть, допустим, репозиторий, который вам очень сложно будет повторить. Потому что там нет места функциональщине. Там выключен Нейгл и включено на максимум байтоёбство. И даже если получится, в 1800 строк вы на хаскеле ну никак не уложитесь. Это не его.

Да, ещё раз скажу. 5000 строк - это даже не маленький проект. Это крохотный. Не надо говорить, что хаскель компактный. Это не так.

И скажите, какой апи для получения комментов.

Функция loadComment. Пришла из васиной библиотеки, которую он написал для работы со своим сервисом.

Если это какой-то бекенд, то давайте сначала ПОЛНЫЙ текст на хаскеле обработки апи. Вместе с загрузкой дерева, вместе с отправкой комментов. Вместе с сериализацией. А потом будем сравнивать именно полный текст.

Вы не понимаете, что такое модельная задача. Ну ок.

Я так и сделал.

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

В чём проблема? Вы даже не потрудились сказать, где может ошибка возникнуть.

В функции loadComment, как это было ясно из её типа.

Потому что там нет места функциональщине. Там выключен Нейгл и включено на максимум байтоёбство.

Оххх бро…

Сколько раз я занимался байтоёбством на хаскеле и получал код, работающий на уровне плюсов (а иногда чутка быстрее) — не перечесть. В худшем случае объём вычислительного кода сравним с объёмом кода на плюсах.

Да, ещё раз скажу. 5000 строк - это даже не маленький проект. Это крохотный. Не надо говорить, что хаскель компактный. Это не так.

Он делал больше, чем реализация соседней группы, делавшей ту же задачу на плюсах, которую до меня припрягли делать то же, которая делала это втроём, которая делала это пару лет (я справился за пару месяцев чистого времени как сайд-проект), и которая это в итоге так и не доделала до конца. Сколько там строк кода было точно, я не знаю, но точно больше нескольких десятков тысяч.

Функция loadComment. Пришла из васиной библиотеки, которую он написал для работы со своим сервисом.

Полный формат функции. Что принимает, что выдаёт, чем кидается.

Выходной формат, который от меня требуется. В каком виде это передаётся дальше.

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

Я очень рад, но проект на 5к строк нельзя назвать большим. И средне-большим. И даже средним. Никак.

Полный формат функции. Что принимает, что выдаёт, чем кидается.

В типах же описано! Принимает Int, выдаёт либо объект типа NetworkError (дан свыше), либо объект типа Comment (тоже).

Вам нужно получить Tree Int (дерево с интами в узлах), для каждого инта загрузить комментарий через loadComment, и выдать либо список возникших ошибок (если возникла хотя бы одна), либо дерево, где каждый Int заменён на соответствующий комментарий.

Я очень рад, но проект на 5к строк нельзя назвать большим. И средне-большим. И даже средним. Никак.

Так а проект на 50к строк можно назвать средним?

В типах же описано! Принимает Int, выдаёт либо объект типа NetworkError (дан свыше), либо объект типа Comment (тоже).

Неправильно. Давайте ещё раз

Неправильно. Давайте ещё раз

Что именно неправильно-то?

А, чисто для протокола — на исключения можете забить и считать, что их нет. Но если вам удобнее с исключениями, то можете и с исключениями, конечно, но тогда вам придётся отобрать те, которые соответствуют только NetworkError, и собрать их все.

Сигнатура какая у функции?

где каждый Int заменён на соответствующий комментарий

Чтобы два раза не вставать. Int нельзя заменить на строку. И на Comment тоже.

Сигнатура какая у функции?

Вы по кругу просто ходите, что ли? Сигнатуру я уже тоже выше писал:

loadComment :: Int -> IO (Either NetworkError Comment)

Чтобы два раза не вставать. Int нельзя заменить на строку. И на Comment тоже.

Не понял. Кто запретил?

GHCi, version 9.12.2: https://www.haskell.org/ghc/  :? for help
λ> import Data.Tree
λ> t = Node 10 [Node 20 [], Node 30 []]
λ> :t t
t :: Num a => Tree a
λ> :t fmap show t
fmap show t :: Tree String
λ> fmap show t
Node {rootLabel = "10", subForest = [Node {rootLabel = "20", subForest = []},Node {rootLabel = "30", subForest = []}]}

Вот я вам заменил число на строку.

Вы по кругу просто ходите, что ли? Сигнатуру я уже тоже выше писал:

Я на шарпе пишу, нахер мне сигнатура хаскеля?

Не понял. Кто запретил?

У шарпа есть тип возвращаемого значения. Входит в сигнатуру функции. Там ещё входные параметры, но допустим на входе int id. Какой тип возвращаемого значения?

Я на шарпе пишу, нахер мне сигнатура хаскеля?

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

Впрочем, возможную сигнатуру шарпа я тоже приводил выше.

У шарпа есть тип возвращаемого значения. Входит в сигнатуру функции.

Пока ничего нового, всё как на хаскеле.

Там ещё входные параметры, но допустим на входе int id. Какой тип возвращаемого значения?

У загружатора одного комментария? Либо NetworkError, либо Comment. Как выражается «либо» в шарпе идиоматически, я не знаю. В хаскеле это Either.

Всмысле перевести? Мне функцию дали. По условию. Где сигнатура?

Если по своему, то во-первых, возвращение NetworkError - чушь собачья. При живых эксепшенах.

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

В третьих, эксепшены всегда разделяются на критичные и некритичные. Если критичный, то дальше смысла нет. Например ошибка кредов - вот зачем дальше выбирать эти комменты? Такие пробрасываются наверх, а не отдаются.

Как выражается «либо» в шарпе идиоматически

Никак. Это не хаскель.

И шарп статически типизированный. Там нельзя инт поменять на коммент.

Всмысле перевести? Мне функцию дали. По условию. Где сигнатура?

А тут вам в условии дали фору: сигнатуру можете выбрать как вам удобнее.

Если по своему, то во-первых, возвращение NetworkError - чушь собачья. При живых эксепшенах.

Это чтобы потом вместо того, чтоб просто посмотреть на тип функции, ходить спрашивать «а что функция кидает?», как вы уже сделали выше?

Но, впрочем, я и про это написал! Во:

Но если вам удобнее с исключениями, то можете и с исключениями, конечно, но тогда вам придётся отобрать те, которые соответствуют только NetworkError, и собрать их все.

Во-вторых, запрос коммента по сети по одному - чушь собачья.

Вы реально по кругу ходите. Мы с вами это уже обсудили.

Например ошибка кредов - вот зачем дальше выбирать эти комменты?

Вы точно не понимаете, что такое модельная задача.

Решите уже эту, наконец, и обсудим границы применимости, усложнения и прочее.

Никак. Это не хаскель.

ФП что-то быстро сдулось. А аналог уже даже в плюсы завезли в виде std::expected.

И шарп статически типизированный. Там нельзя инт поменять на коммент.

Ну хаскель-то известен своей динамической типизацией, конечно.

В данном случае это значит «вернуть такое же по форме дерево, но в котором на данной позиции вместо инта лежит соответствующий этому инту комментарий». Сорян за функциональный жаргон.

Это чтобы потом вместо того, чтоб просто посмотреть на тип функции, ходить спрашивать «а что функция кидает?», как вы уже сделали выше?

Современные IDE показывают, какие эксепшены может кинуть та или иная функция. В каких-то языках в комментах пишут. В java принудительно нужно указывать в сигнатуре, какие эксепшены можешь кинуть.

А тут вам в условии дали фору: сигнатуру можете выбрать как вам удобнее.

Окей, я выбираю сигнатуру LoadComment(ref Node node). Тогда всё сведётся к одному рекурсивному вызову.

Окей, хорошо. Пусть там на входе айди, пусть на выходе коммент. Иногда эксепшен. Перехватываем только NotFound, оставльные прокидываем наверх. Допустим. Хотя нет. Тогда смысла нет сохранять текст ошибки. Будем все ловить.

class Node
{
	public int id;
	public string? comment;
	public string? error;
	public List<Node> children = new();

	void LoadComments(Node node, Func<int, string> loadComment)
	{
		try { node.comment = loadComment(node.id); }
		catch (Exception ex) { node.error = ex.Message; }

		foreach (var child in node.children)
			LoadComments(child, loadComment);
	}
}

Вот собственно структура с рекурсивным методом загрузки

Сначала по коду — это, конечно, лол. Код превзошёл мои ожидания, но не в ту сторону, в которую вы думаете.

  • У вас почему-то связана концепция «рекурсивная структура данных такой-то формы» и «что может лежать в каждом узле этой структуры»: Node (в ваших ООПных терминах) сильно связано и с хранением детей, и с хранением конкретных данных в узле.
    Сравните с определением Tree/Node, которые я давал выше.

  • Концепция «обойти дерево» тоже связана с содержимым этого дерева.
    Но я всё эти связности могу понять: книги по ООП учат разделять кошечек, собачек, квадратики и прямоугольнички, а не действия. В ООП вообще не принято думать в терминах действий, потому что действия не являются first-class. Чтобы выразить действие, апологет ООП (ООПологет?) навернёт десяток паттернов из фабрик визиторов стратегий.

  • У вас никак не выражаются инварианты на уровне типов (да и типов нет, stringly-typed комментарий и ошибка не считаются). Комментарий может быть, а может и не быть. Ошибка может быть, а может и не быть. Они могут не быть оба сразу. Они могут быть оба сразу. По чтению определения данных ничего непонятно, надо читать алгоритм, чтобы понять, что происходит. И в вызывающем коде надо проверять явно их наличие (забыл проверить ошибку — твоя проблема).
    Сравните с Either NetworkError Comment — там всё сразу видно, и обратиться к комментарию, если его нет, невозможно.

  • Собсна, дерево до загрузки комментов и дерево после загрузки комментов на уровне типов тоже никак не отличаются.
    Сравните с Tree Int и Tree Comment (или Tree (Int, Comment), если вашей душе так угодно). Type-driven development? Не, не слышали.

  • LoadComments почему-то является нестатическим членом класса, хотя к полям экземпляра класса никак не обращается. Как читающему только тип функции понять, как связана та Node, которая this, и та Node, которая передана как node?

  • Func<int, string>, конечно, понятнее и синтаксически чище, чем Int -> String (который у меня с лигатурами выглядит как Int → String) Читать Func<int, List<int>, string> тем более легче, чем Int → [Int] → String — заодно глаза тренируются скобочки считать и различать, в старости пригодится как защита от Альцгеймера.

  • Кстати, как у вас выражается rank-n-полиморфизм? Как будет выглядеть тип функции, который на хаскеле выражается как forall a. Show a => a -> a -> String, скажем? Ну, то есть, функция, которая принимает два аргумента (одного и того же) любого типа, реализующего интерфейс IShow(и возвращает строку). Что нужно будет написать вместо ... в Func<...>?
    Ой, я по ассоциациям ещё про multiparam typeclasses вспомнил, но не стоит вскрывать эту тему, это уже будет просто избиение младенцев.

  • Я не могу посмотреть на результат LoadComments и понять, произошла хотя бы одна ошибка в дереве или нет (это вообще было одним из условий, про которые я регулярно говорил). Мне после вызова LoadComments надо будет обойти дерево ещё раз. И чтобы собрать ошибки в список, его тоже надо будет обойти.

  • try/catch ловит все ошибки и теряет информацию о типе, тупо записывая Message. Я просил весь тип ошибки целиком.
    Олсо, это совершенно неподдерживаемый код: если раньше loadComment по каким-то причинам не кидала ошибок (например, потому что разработчик как настоящий фанат OOP, TDD, SOLID, CBT и SPH сначала всё мокал, а моки ничего не кидали, скажем), и поэтому вы не написали try/catch, то после того, как loadComment таки начнёт кидать ошибки, вы, конечно, свой код не обновите, потому что компилятор вам про это не скажет.
    Зачем так жить в 2025-м году?

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

Сравните с моим вариантом, который я себе позволю одной строкой, раз цепочки функций вам норм:

loadComments :: Tree Int → IO (Validation (NonEmpty NetworkError) (Tree Comment))
loadComments = fmap sequenceA . traverse (fmap (liftError pure) . loadComment)

Всё. Дерево (Tree, это тип из стандартной либы) обходится (в traverse, это стандартная функция). Ошибки собираются (в sequenceA, это стандартная функция). Разделение на ошибки, комментарии и структуру есть. Validation (единственная не оч стандартная, но достаточно известная штука, которую я подключил: вопрос дописывания validation в package.yaml) гарантирует семантику «либо ошибки, либо дерево комментов». NonEmpty (тоже тип из стандартной либы) гарантирует, что есть хотя бы одна ошибка.

Господи, да этот код даже тестировать не надо, он просто работает. Там нечему ломаться и негде ошибиться.

Если не наглеть и сделать, как я бы сделал для прода, то я вынес бы обёртку для loadComment отдельно, и всё вообще читабельно без лишних скобочек:

loadComments :: Tree Int → IO (Validation (NonEmpty NetworkError) (Tree Comment))
loadComments = fmap sequenceA . traverse loadCommentValidating
  where loadCommentValidating = fmap (liftError pure) . loadComment

Современные IDE показывают, какие эксепшены может кинуть та или иная функция.

Это алгоритмически неразрешимая задача. И я обычно пулл-реквесты, например, ревьювлю в интерфейсе гитхаба, и диффы смотрю там же (или в консоли c git show / git diff). Там IDE как-то не оч работает.

В каких-то языках в комментах пишут.

Комменты-то у нас, конечно, не устаревают и проверяются компилятором при каждой сборке.

Ещё в комментариях можно типы переменных писать (как в старом JS или во Flow, только без запуска тайпчекера Flow). Можно даже имена там писать, как в ассемблере!

mov ecx, [ebx] ; в cx счётчик цикла

В java принудительно нужно указывать в сигнатуре, какие эксепшены можешь кинуть.

Да, checked exceptions — самая близкая из упомянутых вами вещей к Either. Правда, костыльная, без полиморфизма по экзепшонам, и так далее, поэтому на практике малоюзабельная.

Я вот читаю весь этот снисходительный пафос и не пойму... А где решение в 10 раз короче? То, что я написал - работает.

То, что там именно сообщения вылавливаются, без типов ошибки, так извините, это я так понял. Так объяснено было. Можно и с типом ошибки отлавливать, вообще никаких вопросов. Вместо string будет Exception. И что?

Я напомню, этот пример специально для haskell. У вас loadComments использует готовые структуры языка/библиотек и выдаёт данные, подогнанные к условиям задачи.

В ООП вообще не принято думать в терминах действий

Вот так говорить вообще не надо. Это в крайней степени некорректно. И говорит разве что о вас и вашем восприятии.

Делать выводы по оторванному от реальности куску кода - это какая-то специальная олимпиада. Нет, серьёзно. Это действительно похоже на какую-то олимпиаду, когда люди соревнуются в написании куска кода по условиям задачи.

Читать Func<int, List<int>, string> тем более легче, чем Int → [Int] → String — заодно глаза тренируются скобочки

Это тоже некрасивый приём. В моём коде таких конструкций нет. Вы её сами продумали.

Я вижу, что чьё-то самолюбие задето. Настолько, что потребовалось вот как-то доказать превосходство хаскеля над шарпом. Самоутвердиться так сказать. Причём не самыми адекватными способами.

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

Вот это мне вообще не понравилось. Прямо какой-то дон в белом плаще смотрит, как там крестьяне палкой землю ковыряют.

Я сейчас вот что сделаю. Просто сверну разговор и на этом закончим. У меня нет никакого желания дальше его продолжать. Уж очень токсично получается.

Я вернусь в свой несовершенный мир, где есть гуй авалонии, есть разор пейджес и буду дальше писать свой несовершенный продукт. А вы пойдёте своим путём и будете писать свои небольшие, но совершенные проекты. Идёт?

Я вот читаю весь этот снисходительный пафос и не пойму... А где решение в 10 раз короче?

Сразу после списка.

То, что я написал - работает.

Не работает, и я написал, почему, в половине пунктов списка.

И не работает с точки зрения поддерживаемости крупных проектов в долгосрочной перспективе, и об этом вторая половина списка.

У вас loadComments использует готовые структуры языка/библиотек и выдаёт данные, подогнанные к условиям задачи.

А если бы там хэшмапы ещё участвовали, то вы бы сказали, что пример подогнан под хэшмапы? Или хэшмапы типа можно, потому что они в C# есть из коробки?

Ну возьмите готовую библиотеку с реализацией деревьев, я разве против?

Вот так говорить вообще не надо. Это в крайней степени некорректно.

По причинам несоответствия этого утверждения объективной реальности, или потому, что звучит неприятно?

Если второе, то я-то думал, что мы тут с вами открыто и смело и прямо в лицо, 359!

Делать выводы по оторванному от реальности куску кода - это какая-то специальная олимпиада. Нет, серьёзно. Это действительно похоже на какую-то олимпиаду, когда люди соревнуются в написании куска кода по условиям задачи.

Демонстрационный пример про выразительность языка.

Говоришь про примеры из практики (которые в комменты не влезают) — не подходит. Приводишь примеры, которые в комменты влезают — не подходит. Ну вы скажите, что это всё для вас вопрос веры, и я отстану тогда, ё-моё.

Это тоже некрасивый приём. В моём коде таких конструкций нет. Вы её сами продумали.

Конечно, лучше писать «типичной ФП-сплющенной чёрной магией, где за смысл отвечают знаки препинания, а не слова».

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

Я вижу, что чьё-то самолюбие задето. Настолько, что потребовалось вот как-то доказать превосходство хаскеля над шарпом.

Любое предметное обсуждение — задетое самолюбие и потребность в доказательстве чего-то там? Лол.

Вот это мне вообще не понравилось. Прямо какой-то дон в белом плаще смотрит, как там крестьяне палкой землю ковыряют.

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

Идёт?

Нет, это скучно :(

По причинам несоответствия этого утверждения объективной реальности, или потому, что звучит неприятно?

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

я вообще так-то матершинник, вырос на двач-adjacent-культуре

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

Мы начали с того, что 5к строк проекта - это маленький проект. Собственно, на этом и остановились.

То, что ваш проект на 5к строк хоть немного близок по объёму к моему на 50к - это, мягко говоря, неправда. А в контексте управления сложностью объём проекта - это ключевой параметр. Без объёма никакого управления сложностью и не нужно. Крохотные скрипты на 500 строк вполне обходятся и без ORM и без ООП и даже без статической типизации. Это всё инструменты, которые начинают работать позже.

Потому что это характеристика личных качеств людей, работающих в парадигме ООП.

«Не принято воспринимать X как Y» — это не характеристика личных качеств.

Равно как и пример про кошечек и собачек. Вам откуда знать, по каким книгам я учился?

Где я написал про книги, по которым вы учились?

То, что ваш проект на 5к строк хоть немного близок по объёму к моему на 50к - это, мягко говоря, неправда.

Почему? Практика показывает, что это достаточно точная оценка.

Почему? Практика показывает, что это достаточно точная оценка.

Потому что 5 и 50 - это цифры разного порядка. Я смотрю сейчас на свой проект. Вот основная часть, где 36к без тестов. Вы её никогда в жизни не уложите в 3600 строк. Никак. Ни на каком языке. Оговорка: без применения грязных хаков, вроде стекования операторов до двухтысячной позиции.

Там слишком много нюансов, слишком много всего, чего нужно предусмотреть. Это сложно объяснить на словах.

И нет, ФП - это не волшебная таблетка, которая может драматически снизить сложность проекта. Иначе мы бы видели засилие ФП в больших и сверхбольших проектах. А там традиционно ООП.

Потому что 5 и 50 - это цифры разного порядка. Я смотрю сейчас на свой проект. Вот основная часть, где 36к без тестов. Вы её никогда в жизни не уложите в 3600 строк.

А я смотрю на свой проект, где 5к делало больше, чем несколько десятков к соседней команды. И? Какой вывод?

Иначе мы бы видели засилие ФП в больших и сверхбольших проектах. А там традиционно ООП.

Если под «традиционным ООП» понимать «нафигачили паттернов по книжкам, в итоге вообще ничего не понятно», то да, с таким я встречался.

Если чуть серьёзнее, то разгадка одна: на мейнстримных императивных языках можно быстрее выдать что-то, как-то более-менее работающее. Разработка софта — это жадный алгоритм, поэтому имеем то, что имеем.

А я смотрю на свой проект, где 5к делало больше, чем несколько десятков к соседней команды. И? Какой вывод?

Пример худшего - плохой вариант. У меня есть ещё одно объяснение, но оно вам не понравится. Я промолчу, потому что не знаю деталей.

Если под «традиционным ООП» понимать «нафигачили паттернов по книжкам, в итоге вообще ничего не понятно», то да, с таким я встречался.

Пример худшего - плохой пример. ООП - инструмент. Им можно хорошо уметь пользоваться, можно пользоваться во вред себе.

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

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

Кстати, это больше подошло бы для языков вроде js, python, или даже php. Там низкий порог входа за счёт отсутствия ограничений по типам, необязательного структурирования данных. Что вижу - о том и пою. Шарп, java - это языки другой весовой категории, где есть продвинутый инструментарий для управления сложностью. С более высоким порогом входа. Кресты отдельно - их вообще никто до конца не понимает.

Пример худшего - плохой вариант.

Это пример, показывающий, почему «крупные проекты» по вашим определениям вам не видно.

У меня есть ещё одно объяснение, но оно вам не понравится. Я промолчу, потому что не знаю деталей.

Мне скоро спать пора, а как я уснуть смогу, не зная, что там за объяснение такое?

Пример худшего - плохой пример. ООП - инструмент. Им можно хорошо уметь пользоваться, можно пользоваться во вред себе.

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

Говорите, как будто это что-то плохое.

Хорошее, конечно. Мне потом неплохо платят, чтобы с результатами такой стратегии разбираться.

У чистого ФП гораздо хуже инструментарий управления сложностью.

Потому что ведь нельзя, чтобы просто в типах всё было видно, да? Надо, чтобы мышкой надо было поелозить в IDE, и чтобы комментарии были устаревшие. Ведь основная сложность для нас — это поиск работы, соответственно борьба со сложностью — это обеспечение job security.

Там низкий порог входа за счёт отсутствия ограничений по типам, необязательного структурирования данных. Что вижу - о том и пою. Шарп, java - это языки другой весовой категории, где есть продвинутый инструментарий для управления сложностью.

Кажется, вы начинаете до чего-то догадываться.

Мне скоро спать пора, а как я уснуть смогу, не зная, что там за объяснение такое?

Ну ок. Надо же человека отпустить спать. Ваше представление о сопоставимости результата может сильно отличаться от реального. Утверждать не буду, надо знать детали.

Если инструмент устроен так, что им легко отрезать себе пальцы, то это плохой инструмент.

Нет. Это ошибочная логика. Впрочем на вкус и цвет. Гугл создал Го, в котором не разгуляешься и не выстрелишь себе в ногу. Но в этом же его беда. Впрочем, гуглу виднее - наверное так быстрее набирать кодеров.

Потому что ведь нельзя, чтобы просто в типах всё было видно, да?

Инструментарий - это не только типы и конструкции языка. Это дебаггер, компилятор, это выбор гуя. Со всеми этими пунктами у хаскеля плохо. Например, авалонии там нет и скорее всего никогда не завезут. ВПФ, Блейзор - это тоже инструменты для всего того же. Хотрелоад не то же самое, что mvvm. Однако и то и другое - инструменты управления сложностью. Как тёплое и мягкое - это комфорт.

Ваше представление о сопоставимости результата может сильно отличаться от реального. Утверждать не буду, надо знать детали.

Ну это ж не поэзия и не философия, тут есть объективно измеримые критерии успеха.

Например, есть набор тестовых программ, которые компилятор (эта штука на 5к строк — компилятор для предметно-специфичного языка, да) должен успешно компилировать. Если реализация A компилирует, скажем, 90% набора в правильно исполняемый код, а реализация B — 40%, и на пересечении этих множеств исполняемый код от реализации A работает настолько-то быстрее, то вполне можно сказать, что результат не просто сопоставим, а кратно лучше.

Нет. Это ошибочная логика. Впрочем на вкус и цвет. Гугл создал Го, в котором не разгуляешься и не выстрелишь себе в ногу. Но в этом же его беда. Впрочем, гуглу виднее - наверное так быстрее набирать кодеров.

Я тоже не люблю го. Но да, задачи гугла го решает отлично: это хороший инструмент по критерию «можно взять вчерашнего выпускника и посадить писать микросервисы». Плюсы — хороший инструмент по критерию «широкая доступность работы и интеграция с легаси-системами». JS — хороший инструмент по критерию «работает в браузере». Хаскель — хороший инструмент по критерию «о коде легко рассуждать, глядя только на типы, код сразу работает после рефакторинга, поддерживается type-driven development, и так далее». Он не обязан при этом быть хорошим для использования вчерашними выпускниками, или работы в браузере, или интеграции с легаси-системами.

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

У меня всё больше впечатление, что вы не знаете, о чём говорите. Почему с компилятором-то плохо, например? ghc — это лучшее, что у меня было.

Выбор гуя — да, здесь согласен. Гуй на хаскеле я бы делать не стал. Правда, я бы его ни на чём бы делать не стал, но это другой вопрос.

И вы говорите «гуй, авалония и хот релоад», а я говорю «репл, беготня по деревьям, и готовые алгоритмы унификации или монадический байесовский инференс».

Если реализация A компилирует, скажем, 90% набора в правильно исполняемый код, а реализация B — 40%

То обе не годятся и сравнивать их бесполезно.

Возможно в варианте B намного более сложные задачи решены. Вот пусть и там и там будет 100%, тогда можно сравнивать.

Я тоже не люблю го.

Я не про то. Циркулярка может отпилить пальцы. Болгарка вообще может порвать лицо, если туда поставить диск по дереву. Ну ты же сам дурак, что туда диск поставил не тот. Сам дурак что пальцы сунул. Инструменты-то хорошие. Что самое странное (нет), чем мощнее инструмент, тем больше вреда он может долбоёбу нанести. Поэтому логика "инструмент, которым можно отрезать пальцы - плохой инструмент" - некорректная в самой своей основе. Некоторые люди умудряются стеклянный предмет разбить и пальцы порезать. Стекло плохое?

У меня всё больше впечатление, что вы не знаете, о чём говорите.

У меня впечатление, что вам тяжело не переходить на личности. Держитесь, я верю. Ghc очень долго компилирует и жрёт много памяти. Я такую информацию слышал. Может просто я привык к дотнету, где всё практически моментально. Может стоило сказать, что ghc нормальный, а компилятор в dotnet - отличный. Да?

И вы говорите «гуй, авалония и хот релоад», а я говорю «репл, беготня по деревьям, и готовые алгоритмы унификации или монадический байесовский инференс».

Как в том анекдоте "а зачем нам весь этот тюнинг в зоопарке?". Ну серьёзно, вот посмотрите на уровень, за который то и то отвечает. Вот зачем нужна беготня по деревьям, когда нет гуя? Ну допустим, в паре мест бы пригодилось. Но я и так дерево обойду, чай не на брейнфаке. А без хорошего гуя... ой, всё

То обе не годятся и сравнивать их бесполезно.

Да, если один программист за X времени починил 40% сломанных тестов, а другой — 90%, то они оба одинаково негодные программисты, их сравнивать нельзя. Надо починить 100%, и потом сравнивать.

Тем более, что на практике может встречаться эквивалентные 80%, скажем, которые реализацией A уже покрываются, а B — нет.

Возможно в варианте B намного более сложные задачи решены.

Как я и говорил выше, это для вас вопрос веры: вы, «не зная книг по которым я учился», делаете выводы.

Я не про то. Циркулярка может отпилить пальцы.

Да. Поэтому если что-то можно выпилить при помощи и циркулярки, и miter saw (хз как по-русски), то я предпочитаю miter saw, ровно потому что она от пальцев дальше.

Что самое странное (нет), чем мощнее инструмент, тем больше вреда он может долбоёбу нанести.

Из этого не следует, что любой инструмент, могущий нанести много вреда долбоёбу, заведомо более мощный.

Поэтому логика [...]

[...] хромает в вашем рассуждении выше, сорян.

У меня впечатление, что вам тяжело не переходить на личности.

Для протокола, переход на личности — это аргументация вида «ты неправ потому, что ты идиот и хочешь свою мамку» (хотя последнее скорее фрейдизм, но неважно). Высказывание «ты неправ потому, что A и A ⇒ B известно нам обоим, поэтому из этого следует B, но ты делаешь вывод ¬B» не является, например, переходом на личности. Кстати, переходом на личности не будет даже последующее высказывание «поэтому ты идиот». Вопрос исключительно в том, стоит характеристика личности слева или справа от значка ⇒.

Но это, впрочем, неважно, потому что апелляция к личности — это метод ведения спора, а спор — он вокруг потенциально обсуждаемых высказываний, а фраза «у хаскеля всё плохо с компилятором» поддающимся обсуждению высказыванием не является. У хаскеля всё плохо с компилятором. У C# всё плохо с компилятором. У C++ всё плохо с компилятором. Почему? Я художник, я так вижу.

Был бы другой разговор, если б вы сразу написали, скажем, что…

Ghc очень долго компилирует и жрёт много памяти. Я такую информацию слышал.

… но очень долго — это сколько? И сколько другие компиляторы языков с не менее слабыми по выразительной силе системами типов компилируют с -O0? (известные мне — дольше) Сколько компилируют компиляторы с -O3, которые выдают похожий по производительности код из похожего кода по высокоуровневости?

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

И судить о готовности языка к проду по скорости компиляции и потреблению памяти — плохая идея. У вас тогда и C++ не готов.

Может просто я привык к дотнету, где всё практически моментально.

Я привык к LSP, где всё практически моментально. Пишешь код, а тебе сразу красным подчёркивается, где ты неправ. С какой скоростью работает компилятор — совершенно неважно, потому что саму сборку нужно запускать очень редко.

Может стоило сказать, что ghc нормальный, а компилятор в dotnet - отличный. Да?

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

Ну серьёзно, вот посмотрите на уровень, за который то и то отвечает. Вот зачем нужна беготня по деревьям, когда нет гуя?

Ну, не знаю, когда вы компиляторы пишете, то за всем?

Мне вообще ни разу в жизни не платили за проекты, где был бы гуй (или где хотя бы пригодился бы гуй). Пет-проект у меня такой, с гуями, ровно один, и я его начал школотроном в 15 лет, когда не знал ничего лучше.

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

Ну да, выше уже обошли. 10 строк неподдерживаемого кода вместо одной.

И это я ещё не говорил о магии вроде uniplate.

их сравнивать нельзя. Надо починить 100%, и потом сравнивать.

Вот эта часть правильная. Делать выводы, что программисты - негодные неправильно. Делать выводы что компилятор не понимающий 100% - негодный правильно. В чём разница? Компилятор может быть дописан, программист - нет.

Как я и говорил выше, это для вас вопрос веры: вы, «не зная книг по которым я учился», делаете выводы.

Это не вопрос веры. Я сказал "возможно в варианте В были решены более сложные задачи". Где здесь вера? Это за что меня в религию записывают?

То, о чём я говорю, называется допущение. Я его привёл, чтобы доказать, что вывод "90% поддержки конструкций всегда больше 40%". Это не так, области могут быть разными, вес у задач разный.

то я предпочитаю miter saw

Торцовочная пила. У неё другие задачи, как видно из названия. Циркулярки тоже, кстати, современные с защитой. Они тоже бывают стационарными, как торцовочная и ручными.

И судить о готовности языка к проду по скорости компиляции и потреблению памяти — плохая идея.

Во-первых я так не делал. Я говорил про управление сложностью. Слов "не готов" я не писал.

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

А что, бывает иначе?

Когда у вас там в сишарпе тайпчекер будет такой же, как в хаскеле

В шарпе не будет никогда такого же тайпчекера. Потому что шарп - не хаскель. Это не значит, что его там нет или что он хуже.

10 строк неподдерживаемого кода вместо одной.

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

Делать выводы что компилятор не понимающий 100% - негодный правильно.

Вы делаете не этот вывод. Вы делаете вывод, что до дописывания до 100% сравнивать нельзя.

Начнём с того, что это не соответствует действительности: в компиляторах есть известные баги, компиляторы известно реализуют стандарты/спеки не до конца, и так далее, и это не мешает компиляторами пользоваться. Этот ваш cl.exe вроде как не поддерживал такую базовую штуку, как two-phase lookup, и ничего, люди его не то что сравнивали, люди им пользовались и разрабатывали работающие продукты.

Это не вопрос веры. Я сказал "возможно в варианте В были решены более сложные задачи". Где здесь вера? Это за что меня в религию записывают?

Вера в нахождении всё менее реалистичных и всё более неопровержимых причин. Неопровержимых потому, что вы не воспримете, конечно, утверждение в духе «нет, множество проходящих у них тестов было собственным подмножеством проходящих у меня тестов».

Торцовочная пила. У неё другие задачи, как видно из названия.

Мне не видно, я её для выпиливания плинтусов купил вообще и не шарю, но неважно.

Есть пересечение задач, которое может быть решено обоими видами пил. На этом пересечении эти вещи вполне можно сравнивать.

Во-первых я так не делал. Я говорил про управление сложностью. Слов "не готов" я не писал.

Окей, справедливо. Переформулирую в ваших терминах: язык C++ не позволяет управлять сложностью потому, что там компилятор жрёт много памяти и компилирует долго. Правильно, да?

Ну и с хот релоадом там тоже довольно печально (dlopen/dlclose — это не хот релоад).

А что, бывает иначе?

В некоторых плюсовых проектах — да. Там либо всё красненькое, потому что LSP отвалился, либо ничто не красненькое, потому что LSP снова отвалился, но в другую сторону.

Это не значит, что его там нет или что он хуже.

Наоборот, это значит, что с точки зрения управления сложностью (типы — они для этого) сишарп в этом аспекте хуже.

Потому что, во-первых, у меня не 10 строк обхода дерева.

Но у вас и задача до конца не решена.

Во-вторых у вас не одна строка.

В данном случае строку с типом можно вообще убрать. Воспринимайте её как комментарий.

Вы делаете не этот вывод.

И этот тоже. Я об этом прямо сказал.

Вы делаете вывод, что до дописывания до 100% сравнивать нельзя.

Правильно. А ещё я объяснил, почему. Это я ещё не касался других параметров. Скажем так, одну и ту же работу можно сделать множеством способов и помимо очевидного процента разборки токенов есть ещё сотни нюансов.

Начнём с того, что это не соответствует действительности: в компиляторах есть известные баги, компиляторы известно реализуют стандарты/спеки не до конца

Я устал от демагогии. Речь шла про, цитирую " 90% набора в правильно исполняемый код ".

Вера в нахождении всё менее реалистичных и всё более неопровержимых причин.

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

Возвращаю: я утверждал, что ООП нужен для управления сложностью. Вы утверждали, что ваш "большой" проект на 5к строк справляется без ООП. Я утверждаю, что проект на 5к строк не может считаться большим. Какая нахуй вера?

То, что я пишу - это вполне научные методы. Есть ваше утверждение, что ваш вариант лучше. Есть моё предположение, что возможно вы переоцениваете своё творение. Это ни разу не фантастика, а характерное когнитивное искажение разработчика. Мерить успешность в процентах ключевых слов - бред. При незаконченности обоих вариантов - тем более. Кстати, есть ещё третий вариант people forget exists. Но он вам тем более не понравится.

Мне не видно, я её для выпиливания плинтусов купил вообще и не шарю, но неважно.

Есть пересечение задач, которое может быть решено обоими видами пил. На этом пересечении эти вещи вполне можно сравнивать.

Мне опять придётся к теме возвращать? Речь была про то, что "инструмент, который может отпилить пальцы - плохой инструмет". Так чтож вы плохим инструментом-то пользуетесь?

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

Окей, справедливо. Переформулирую в ваших терминах: язык C++ не позволяет управлять сложностью потому, что там компилятор жрёт много памяти и компилирует долго. Правильно, да?

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

Из этого не следует, что любой инструмент, могущий нанести много вреда долбоёбу, заведомо более мощный.

Отличный вывод, да? А я что, говорил, что следует?

Наоборот, это значит, что с точки зрения управления сложностью (типы — они для этого) сишарп в этом аспекте хуже.

Шарп - статически типизированный язык. У него всё нормально с типами. Они есть, они проверяются на этапе компиляции и всевозможные чекеры работают в реалтайме, пока пишешь код.

Но у вас и задача до конца не решена.

Решена. Метод вызывается? Ошибки ловятся? Дерево обходится? Чего ещё надо?

Хватит уже. Я не школьник и не на олимпиаде. Ваша сраная задачка вообще ничего не говорит ни об ФП, ни о том, что вы умеете делать. Что действительно может показать - ваш готовый продукт. Talk is cheap. Show me your code.

Это я ещё не касался других параметров. Скажем так, одну и ту же работу можно сделать множеством способов и помимо очевидного процента разборки токенов есть ещё сотни нюансов.

Например? Можно конкретнее?

Я устал от демагогии. Речь шла про, цитирую " 90% набора в правильно исполняемый код ".

На что вы ответили, что либо 100%, либо не считается. Так вот, компилятор плюсов (любой) не обрабатывает 100% исходников, соответствующих стандарту, так, как того требует стандарт. В чём разница?

Неопровержимих причин чтобы что?

Чтобы сказать, например, что «есть сотни нюансов».

Возвращаю: я утверждал, что ООП нужен для управления сложностью. Вы утверждали, что ваш "большой" проект на 5к строк справляется без ООП. Я утверждаю, что проект на 5к строк не может считаться большим. Какая нахуй вера?

Нет. Я утверждал, что судить о сложности проекта по количеству строк — бред, потому что разные языки обладают разной выразительной силой, и если две программы решают одну и ту же задачу, то они имеют одинаковую внутреннюю (которая intrinsic) сложность.

Да, проект на 50к строк (решающий то же, что проект на 5к строк) может иметь большую «добавленную» сложность из-за языка, и там ООП действительно может помочь. Но тогда ООП помогает не «решать задачи», не «управлять сложностью проектов», а обкостыливать кривые языки. И это другой тезис (с которым я даже не буду спорить).

Просто, в частности, из вашего тезиса следует, что крупные проекты без ООП невозможны/тяжелы/етц (выберите модальность по вкусу), а из моего — нет.

То, что я пишу - это вполне научные методы.

Научные методы «сотни нюансов» — это на уровне научных методов «святой имени всех электриков помог».

Есть моё предположение, что возможно вы переоцениваете своё творение. Это ни разу не фантастика, а характерное когнитивное искажение разработчика.

Вполне научные данные я уже привёл в виде измеримых критериев: объём кода, процент решаемых задач, производительность, например. Вы говорите, что это всё ерунда и когнитивные искажения. Штош.

Мерить успешность в процентах ключевых слов - бред. При незаконченности обоих вариантов - тем более.

ХЗ, тут вот измеряют, и на эту страницу даже часто ссылаются, хотя там видно, что никто ничего до конца не поддерживает: https://en.cppreference.com/w/cpp/compiler_support.html

Даже C++23 (вышедший почти три года назад уже) целиком не поддерживает ни один компилятор, что не мешает писать -std=c++23 во флагах сборки, сравнивать разные компиляторы, и так далее.

Кстати, есть ещё третий вариант people forget exists. Но он вам тем более не понравится.

Там на самом деле реализовался третий вариант, и он мне на самом деле не понравился, но это уже корпоративная политика, а не тонкости языка.

Речь была про то, что "инструмент, который может отпилить пальцы - плохой инструмет". Так чтож вы плохим инструментом-то пользуетесь?

Потому что остальные ещё хуже [по совокупности доступных критериев]. Неважно, на самом деле, является ли инструмент «плохим» или «хорошим». Важно, лучше он других или хуже (для данных задач). А как вы его назовёте — это неважно, важна лишь его сравнительная позиция.

Пытаться ловить собеседника на этом, «что ж вы плохим пользуетесь» — это как раз та самая демагогия, которая вас вроде как утомила.

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

А к чему эти слова были написаны? Ваш тезис, который вы тут отстаиваете — что $smth не подходит для крупных проектов и управления сложностью. Если вы пишете какие-то тезисы и не пишете явно, что из них следует, то естественно предполагать, что вы ими подтверждаете ваш исходный тезис. Ну или напишите явно, что из них следует-то, и тогда обсудим.

Или, например, инвертируя, как было с инструментами:
Отличный вывод, да? А я что, говорил, что следует?

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

Шарп - статически типизированный язык. У него всё нормально с типами.

Non sequitur. C (без шарпа и плюсиков) — тоже статически типизированный язык, но утверждение «у C всё нормально с типами» — это кекспертиза уровня opennet.

Они есть, они проверяются на этапе компиляции и всевозможные чекеры работают в реалтайме, пока пишешь код.

Важно не то, что они есть, а то, какие поведения они могут запретить. Чем мощнее система типов, тем больше поведений запрещается, тем проще рассуждать о коде, глядя только на типы, и тем больше проверяет компилятор, а не ваш (или мой) величественный взор на PR-ревью.

Решена. Метод вызывается? Ошибки ловятся? Дерево обходится? Чего ещё надо?

Собрать все ошибки, если есть хоть одна, выразить семантику «либо ошибка, либо коммент», и ещё ряд пунктов по списку. Мне его сюда закопипастить?

Например? Можно конкретнее?

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

На что вы ответили, что либо 100%, либо не считается. Так вот, компилятор плюсов (любой) не обрабатывает 100% исходников, соответствующих стандарту, так, как того требует стандарт. В чём разница?

Разница в том, что проценты поддержки семантики нельзя сравнивать напрямую. Я, к примеру, могу написать компилятор, с поддержкой 90% семантики за месяц. С кучей нюансов, да. И говорить, что эти идиоты писали свой годами. Что тоже будет некорректно.

Впрочем, в том месте я имел в виду, что мне всё равно, что моя программа корректно скомпилится на 90% или на 40%. Она всё равно не запустится.

Я утверждал, что судить о сложности проекта по количеству строк — бред

Нет, не бред. Только это работает только в одну сторону. Много строк не означает сложность проекта. Но сложный проект обязательно имеет много строк.

потому что разные языки обладают разной выразительной силой

Не в 10 раз никак.

я уже привёл в виде измеримых критериев: объём кода

Я утверждал, что судить о сложности проекта по количеству строк — бред

Определитесь.

Потому что остальные ещё хуже [по совокупности доступных критериев]. Неважно, на самом деле, является ли инструмент «плохим» или «хорошим».

Циркулярка может пилить доски вдоль. Торцовочная - только поперёк. Остальные ваши рассуждения про "пересекающиеся области" смысла не имеют.

А к чему эти слова были написаны?

Потому что мне надоела демагогия и манипуляция.

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

Нет. I said what I said. Я сказал, что ваше утверждение ошибочно.

Собрать все ошибки, если есть хоть одна, выразить семантику «либо ошибка, либо коммент», и ещё ряд пунктов по списку. Мне его сюда закопипастить?

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

И у вас там в голове ничего не щёлкнуло, когда это писали, не? No bells ringing? Нормально вообще требовать семантики хаскеля от кода на шарпе?

И я не вижу ваших проектов. Ни гитхаба ни продуктов в паблике. И у меня возникает интересный вопрос.

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

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

Разница в том, что проценты поддержки семантики нельзя сравнивать напрямую. Я, к примеру, могу написать компилятор, с поддержкой 90% семантики за месяц. С кучей нюансов, да. И говорить, что эти идиоты писали свой годами. Что тоже будет некорректно.

Если они будут поддерживать не 90, а меньше, и выплёвываемый ими код будет работать медленнее, то вполне себе корректно.

Впрочем, в том месте я имел в виду, что мне всё равно, что моя программа корректно скомпилится на 90% или на 40%. Она всё равно не запустится.

Не понял, что это за «эргодичность» такая? В одном случае компилируется 90% программ, в другом — 40%. Это не то же самое.

Но сложный проект обязательно имеет много строк.

Вот здесь мы и несогласны, и весь тред выше — моя попытка пояснить примерами, за счёт чего именно.

Не в 10 раз никак.

И в 10 раз тоже.

Определитесь.

Давно определился: в первой (моей цитате речь не о сложности, а во второй (вашей) — о сложности.

Циркулярка может пилить доски вдоль. Торцовочная - только поперёк. Остальные ваши рассуждения про "пересекающиеся области" смысла не имеют.

Во-первых, мне ни разу не нужно было распилить плинтуса вдоль. Я их пилил только поперёк, и эту задачу могут решить и циркулярка, и торцовочная, и даже лобзик. Эта задача на пересечении.

Во-вторыю, циркулярка не может пилить доски вдоль сама по себе, потому что вам нужна обвязка, чтобы доску зафиксировать, например. Здесь есть тоже кое-какие параллели с этими нашими ЯП, но не будем об этом.

Нет. I said what I said. Я сказал, что ваше утверждение ошибочно.

Потому что что-то там про сложность и мощность.

У ПО есть всего две задачи: делать то, что требуется и не делать то, что не требуется.

Управление сложностью что-то очень резко сдулось, сразу после ФП в шарпе.

И у вас там в голове ничего не щёлкнуло, когда это писали, не? No bells ringing? Нормально вообще требовать семантики хаскеля от кода на шарпе?

Так я-то и не требую. Я констатирую факт, что у шарпа меньше средств выражения семантики композируемым и проверяемым компилятором образом, чем у хаскеля (а у хаскеля меньше, чем у идриса, например).

И я не вижу ваших проектов. Ни гитхаба ни продуктов в паблике. И у меня возникает интересный вопрос.

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

Так я-то и не требую.

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

Ну и анекдот

Тезис «я ща вас побежду на вашем же поле» очень резко превратился сначала в продолжительное уточнение вопроса, кульминировавшее в на порядок большем количестве кода, вопрос не решающем, а потом в «да это всё игрушечные задачи и не нужно, и важны только 100%-о работающие компиляторы, только если это не компиляторы имеющихся языков».

На разных языках, да.

Тезис «я ща вас побежду на вашем же поле»

Ложь.

порядок большем количестве кода

Ложь.

вопрос не решающем

Ложь.

и важны только 100%-о работающие компиляторы

Ложь.

Тезис «я ща вас побежду на вашем же поле»

Моё поле и мой тезис: «хаскель эффективнее». Ваша цитата: «Так что в данном случае шарп с хаскелем будет 1:1.» Сиречь вы покажете, что я неправ, и победите.

Так что не ложь.

порядок большем количестве кода

10 строк, решающих в лучшем случае половину, против одной-двух в моём случае.

Так что не ложь.

вопрос не решающем

Ошибки в один список не собраны, «либо ошибка, либо комментарий» не выражено.

Так что не ложь.

и важны только 100%-о работающие компиляторы

Ваша цитата: «Вот пусть и там и там будет 100%, тогда можно сравнивать.»

Так что не ложь.

Моё поле и мой тезис: «хаскель эффективнее». Ваша цитата: «Так что в данном случае шарп с хаскелем будет 1:1.»

Опять ложь. "В данном случае" касалось аггрегирования списка и контекст был там.

Ваша цитата: «Вот пусть и там и там будет 100%, тогда можно сравнивать.»

Вот это я говорил.

и важны только 100%-о работающие компиляторы

А это - нет.

Я больше не хочу говорить с человеком, перевирающим мои слова на каждый аргумент. С человеком, который не понимает мои аргументы и продолжает настаивать на своём.

Исходный код ваш мне нужен был, чтобы понять, с кем я вообще спорю. Потому что в интернете много неопытных теоретиков, не видевших в глаза проектов больше 10к строк. Какой смысл мне с такими спорить о методах управления сложностью?

"В данном случае" касалось аггрегирования списка и контекст был там.

А, так это ещё круче. Вы придумали задачу, которой не было и про которую никто не говорил (что такое «агрегирование списка» вообще?), и подменили ей контекст обсуждения. Круто, чё.

А это - нет.

А это как раз следует из контекста беседы, потому что иначе какой смысл отвечать вашими словами про 100% на мои слова про нестопроцентное прохождение тестов?

Исходный код ваш мне нужен был, чтобы понять, с кем я вообще спорю. Потому что в интернете много неопытных теоретиков, не видевших в глаза проектов больше 10к строк. Какой смысл мне с такими спорить о методах управления сложностью?

1:15:33 ~/Programming/work/pact-5 (master) 130 cloc .
     916 text files.
     474 unique files.                                          
     443 files ignored.

github.com/AlDanial/cloc v 2.00  T=0.19 s (2531.6 files/s, 426070.3 lines/s)
-------------------------------------------------------------------------------
Language                     files          blank        comment           code
-------------------------------------------------------------------------------
Haskell                        162           6576           6580          51766
[...]
1:18:03 ~/Programming/work/scverif-exploration (main) % cloc .
     218 text files.
     129 unique files.                                          
      96 files ignored.

github.com/AlDanial/cloc v 2.00  T=0.04 s (3455.8 files/s, 382652.0 lines/s)
-------------------------------------------------------------------------------
Language                     files          blank        comment           code
-------------------------------------------------------------------------------
Haskell                         31            937           1079           4249
Agda                            29            467            204           3577
TLA+                            27            356            282           3086
[...]
1:18:51 ~/Programming/rtdt (master) % cloc .
     233 text files.
     183 unique files.                                          
     220 files ignored.

github.com/AlDanial/cloc v 2.00  T=0.10 s (1802.2 files/s, 243473.3 lines/s)
-------------------------------------------------------------------------------
Language                     files          blank        comment           code
-------------------------------------------------------------------------------
Agda                           131           1662            513          10129
TeX                              8            726            834           7900
Haskell                         30            339             17           1814
[...]

У меня, впрочем, и на плюсах есть пет-проект с топовым вхождением

-----------------------------------------------------------------------------------
Language                         files          blank        comment           code
-----------------------------------------------------------------------------------
C++                               1749          41424          15197         234077

так что я знаю, как не надо.

Сойдёт?

Сойдёт?

Нет. Я когда руководствовался принципом "джентельменам верят на слово", почему-то джентельменам сразу карта пёрла. А мне нет.

Ну, щито поделать десу. С burner-акка мне ещё палиться не хватало.

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

Стоит ли мне верить на слово, что ваш компилятор в 5к строк оказался лучше компилятора на цпп в 10-15к строк (я ничего не перепутал?) и при этом написан всего одним человеком и быстрее?

Я вот что думаю. Есть такой инструмент - dBeaver. Там по осторожным прикидкам, не менее полумиллиона строк кода (лень считать, да). А у меня ormfactory - приложение того же класса с сопоставимой функциональностью, что и dbeaver. Могу ли я говорить, что шарп в десять раз компактнее джавы? Ой, у меня же 37к строк, значит в тринадцать с половиной.

Бред? Бред. Конечно, не могу. Тут каждый пункт "с натяжкой". Шарп "в целом по больнице" компактнее джавы, да. Возможно раза в полтора. Но не в разы.

С хаскелем также. С вашим примером также. Я готов поверить, что на больших проектах хаскель будет компактнее шарпа на процентов 10. 20 уже вряд ли. А потом я уже могу поговорить о том, что компактность ну вот ни разу не бесплатная. В данном случае компактность может легко привести к повышенным когнитивным расходам при работе с кодом. Что в данном случае лучше - я оставляю за рамками. Обычно ищут какую-то точку баланса исходя из своих обстоятельств. У Го баланс смещён в сторону недоверия разрабу, что явно выгодно при большой текучке.

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

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

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

И именно поэтому, например, если бы нам удалось до чего-то договориться хотя бы в игрушечном примере, который показывает один из таких случаев, то можно было бы продолжить обсуждать более крупные примеры (и я бы показал конкретику на uniplate, например, или на автогенерации тестовых примеров, или тому подобных вещах). Но, увы, договориться мы не смогли даже на игрушечном примере, поэтому до чего-то более масштабного не дошло.

Стоит ли мне верить на слово, что ваш компилятор в 5к строк оказался лучше компилятора на цпп в 10-15к строк (я ничего не перепутал?) и при этом написан всего одним человеком и быстрее?

И именно поэтому это на самом деле иррелевантно.

Впрочем, как бы вам мой гитхаб помог сравнить мой проект с проектом другой команды, которого там, очевидно, нет, даже если бы все мои оплачиваемые работы почему-то были бы в опенсорсе?

В данном случае компактность может легко привести к повышенным когнитивным расходам при работе с кодом.

А может и не привести. Может привести к меньшим когнитивным расходам, потому что нужно держать в голове меньше деталей и смотреть на меньшую часть кода.

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

Речь о том, что данное утверждение было подтверждено только "собственным проектом", в мощь которого предлагается поверить на слово.

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

Вот основная часть, где 36к без тестов. Вы её никогда в жизни не уложите в 3600

Смотря какая задача. Мы, было дело, переписали 15 мегабайт исходного кода на C++ в 250 килобайт на Scheme. В том и другом случае код разворачивается автоматическими преобразованиями в 18 мегабайт (в случае C++ это система сборки, в случае Scheme - генерация кода в самой программе) Это, конечно, было непросто, но теперь дорабатывать прикладную логику может даже выпускник церковно-приходской школы.

Иначе мы бы видели засилие ФП в больших и сверхбольших проектах. А там традиционно ООП.

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

Смотря какая задача. Мы, было дело, переписали 15 мегабайт исходного кода на C++ в 250 килобайт на Scheme

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

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

Думать приходится везде и на любом этапе. Я видел много проектов, которые просто умирали под тяжестью техдолга в определённый этап своего развития. Просто не были готовы к взрывному росту сложности.

То, что в начале предпочитают из говна и палок собирать - это логично. Нужно обогнать конкурентов и проверить идею. В этом нет ничего странного.

Это совсем не то же самое, что раздутые кресты.

Вроде то же самое, только на 15 лет моложе.

Точно ни на каком? А на русском или английском? Ну не относятся шарпы к лаконичным языкам, так что нет, не верю.

Чуваку обидно, что полтора калеки понимают его птичий, и еще меньше на нем пишут)

Парадокс Блаба — это не руководство к действию, если что.

В ООП вообще не принято думать в терминах действий, потому что действия не являются first-class. Чтобы выразить действие, апологет ООП (ООПологет?) навернёт десяток паттернов из фабрик визиторов стратегий.

А вот где, кстати, про действия как first-class можно почитать?

А вот где, кстати, про действия как first-class можно почитать?

В любой книжке по ФП :]

Это ж просто higher-order functions. Передаёшь функцию (олицетворяющую действие) в функцию, и всё. Вот тебе паттерн «стратегия».

Каррирование — это просто паттерн «фабрика». И всё.

Аааа, ну это понятно.

В ООП вообще не принято думать в терминах действий, потому что действия не являются first-class.

Они не first-class только в некоторых семействах языков. В тех же javascript или python, которые, кстати, гораздо больше объектно-ориентированы чем c++, c# или java, функции являются first-class

Ну, не совсем уж first в питоне. Литерал функционального типа (т.е. лямбду) можно написать только в самых простых случаях. То что по синтаксису влезет в одну питоновскую строку.

Уж молчу, что функциональные значения нельзя в этих языках нетривиально обрабатывать.

Ну, не совсем уж first в питоне. Литерал функционального типа (т.е. лямбду) можно написать только в самых простых случаях.

Это правда, но, как мне кажется, это больше синтаксическое ограничение вызванное кривостью питона. Так ничего не мешает просто def на соседней строчке использовать.

Уж молчу, что функциональные значения нельзя в этих языках нетривиально обрабатывать.

А где можно нетривиально обрабатывать и как именно?

А где можно нетривиально обрабатывать и как именно?

В Лиспе, конечно. Код - это данные.

Ну и в машинном коде.

Так ничего не мешает просто def на соседней строчке использовать.

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

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

В Лиспе, конечно. Код - это данные.

В питоне тоже код это данные. Этим не так часто пользуются как в лиспе, и синтаксические деревья у питона сложнее, но при желании вы можете с ними работать. Вот небольшой пример:

import ast
import inspect

class ReplacePasswordTransformer(ast.NodeTransformer):
  def visit_Constant(self, node: ast.Constant) -> ast.Constant:
    if node.value != 'password':
      return node
    
    return ast.Constant('<replaced>')

def replace_password(f):
  syntax_tree = ast.parse(inspect.getsource(f))
  f_def: ast.FunctionDef = syntax_tree.body[0]
  f_def.decorator_list = []

  new_syntax_tree = ast.fix_missing_locations(ReplacePasswordTransformer().visit(syntax_tree))

  scope = dict()
  exec(compile(new_syntax_tree, __file__, mode='exec'), globals(), scope)
  value = scope[f_def.name]

  return value

@replace_password
def test():
  print("password")

test()
orenty7@orenty7-laptop:~/projects/python/playground$ uv run main.py
<replaced>

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

Конечно, плюсом питона по сравнению с каким-нибудь Си или Фортраном здесь является то, что методы работы с ast являются частью языка и поэтому отчасти переносимы. Но всё равно постоянство структуры между версиями никто не гарантирует, поэтому практическая применимость ограничена.

Ну справедливости ради, это не код, а получаемое из него ast.

А чем это отличается от лиспа? Там ведь (по крайней мере, в знакомом мне коммон лиспе) тоже не строками вертят, а списками и символами

Но всё равно постоянство структуры между версиями никто не гарантирует

Как и в лиспе, разве нет?

А чем это отличается от лиспа? Там ведь (по крайней мере, в знакомом мне коммон лиспе) тоже не строками вертят, а списками и символами

Так списки и символы и являются синтаксическими примитивами Лиспа. Как кода, так и данных. Зачем строки? Программа статически и динамически является списком. Ни с каким другим представлением кода программист не работает. Ну, строго говоря, он может писать комментарии и другие отклонения от канонической формы, но это не оказывает влияния на сам код и невидимо транслятору.

Как и в лиспе, разве нет?

Нет. Входной язык транслятора Common Lisp окончательно зафиксирован стандартом 30 лет назад, с тех пор всё развитие языка обеспечивается макросами и библиотеками исходных кодов. Любой код совместим снизу вверх и сверху вниз, вопрос только в том, какая его часть лежит внутри дистрибутива транслятора, а какая снаружи. (Понятно, что для внешних связей это не так, но это не проблема Лиспа).

JS — неудивительно, там же вдохновление из лиспов брали :]

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

В ООП вообще не принято думать в терминах действий, потому что действия не являются first-class. Чтобы выразить действие, апологет ООП (ООПологет?) навернёт десяток паттернов из фабрик визиторов стратегий.

Таки являются: в C++ - там есть унаследованный из C указатель на функцию, а ещё есть указатель на функцию-член (метод, иначе говоря), являющий собой половинку делегата из C#, в Delphi - там есть свойства, содержащие обработчики событий, в C# - пресловутые делегаты, в том числе - статические, которые суть функции. Так что действия в этих языках - вполне себе полноценный тип данных.
Просто в ООП действия используются в другой метафоре, нежели в ФП: ими программируют будущее. Переменные типа действия указывают, какой код будет выполнен, если наступит какое-то событие или просто какой код надо будет выполнить позже в рамках стандартного жизненного цикла компонента библиотеки/фреймворка. В той моей статье, где я описывал, что происходит при инициализации веб-приложения в ASP.NET Core этих самых действий - вагон и маленькая тележка. Но вот чистотой функций там не заморачиваются. По крайней мере, в C# - точно: я до сих пор не нашел способа заставить его компилятор проверять отсутствие побочных эффектов у переданных параметров-делегатов (а когда пишешь библиотеку - иногда этого хочется).

Таки являются: в C++ - там есть унаследованный из C указатель на функцию, а ещё есть указатель на функцию-член (метод, иначе говоря)

Этого недостаточно.

Я могу написать функцию, принимающую два инта и возвращающую третий (их сумму). Могу ли я написать функцию, принимающую два указателя на функцию и возвращающую третий, указывающий, скажем, их композицию?

Олсо, для протокола: C++ — мультипарадигменный язык, поэтому для обсуждения «в ООП принято» лучше читать книжки по ООП, а не языки, которые среди прочего поддерживают ООП (но это неточно).

Могу ли я написать функцию, принимающую два указателя на функцию и возвращающую третий, указывающий, скажем, их композицию?

Можете. Только потребуется не функция, а делегат (указатель на объект плюс укахатель на его метод). Потому что эти операнды, из которых надо сделать композицию, надо где-то сохранить, чтобы ими воспользоваться. Там для этого объекта есть умное слово "замыкание", только я вот не в курсе , это то же замыкание, что в теории ФП, или нет. В C# это сделать несложно даже в старом синтакисисе, без стрелочных функций: создаете объект, который содержит оба делегата-параметра, и пишете метод, который который вызывает сначала первый делегат из объекта с переданным ей параметром- целым, затем с результататом, возвращенным этим делегатом, вызываете второй делегат и возвращаете из всего этого метода функции результат выполнения этого второго делегата. А потом возвращаете делегат, состоящий из этого объекта и этого метода.
То есть, всё делалось и в старом синтаксисе. Ну, а когда в синтаксисе появились стрелочные функции (на жаргоне - "лямбды") то это стало делать проще - компилятор сам создает объект замыкания и заполняет его поля. А так как неиспользуемуый объект в C# можно просто бросить (в C++ - нелья) - GC его подберет, то больше ничего делать и не требуется.
В C++, насколько я его представля, сложнее - там память этого объекта надо вовремя освободить, насколько это автоматизировано в современном синтаксисе - не в курсе.

Олсо, для протокола: C++ — мультипарадигменный язык, поэтому для обсуждения «в ООП принято» лучше читать книжки по ООП, а не языки, которые среди прочего поддерживают ООП (но это неточно).

Меня мало интересует, как оно там в теории, в книжках. Я тут следую идеям настоящих программистов (про которых давным-давно писал Эд Пост в Datamation): мне тоже не нужны абстрактные понятия, чтобы делать конкретную работу. Поддерживает ли тот же C# FP - мне как-то без разницы. Я в прошлый раз написал, для какой цели используются эти самые действия в самом что ни на есть ООП - код, вызываемый по событию или там фреймворком когда-нибудь в будущем, - так что область применимости у них есть и без "мультипарадигменности". Типичный пример второго поведения - это делегат инициализации в Lazy<T>.

Или же я не понял, что именно вы называете действиями?

Можете. Только потребуется не функция

— Могу ли я написать функцию?
— Да. Только не функцию.

Гм.

Ладно. Функции вычёркиваем.

В C# это сделать несложно даже в старом синтакисисе, без стрелочных функций: создаете объект

Ура, мы пришли к тому, о чём я говорил изначально: создаёте объект.

Кажется, умные люди в умных ООП-книгах называют это «стратегией» (вернее, частным случаем), и, кажется, я ровно такой пример видел в одной из них (но напрочь не помню, в какой). Там, правда, развешивалась целая иерархия в духе

interface ISomeStrategy
{
  T run(T);
}

class Composite : ISomeStrategy
{
  ISomeStrategy S1;
  ISomeStrategy S2;

  T run(T arg)
  {
    return S2.run(S1.run(arg));
  }
}

вместо простого

compose s1 s2 = \arg → s2 (s1 arg)

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

В C++, насколько я его представля, сложнее - там память этого объекта надо вовремя освободить, насколько это автоматизировано в современном синтаксисе - не в курсе.

Можно в лямбду по значению захватывать, но да.

— Могу ли я написать функцию?
— Да. Только не функцию.

Гм.

Ладно. Функции вычёркиваем

Торопистесь. "Не функция" - это уже детали реализации. А по своей сути это - функция, т.е. - сущность языка, которая преобразует значение из области определения, в значение из множества выходных значений. А уж как она устроена - не столь важно. И в C++ ЕМНИП вместо функции тоже можно использовать объект, который перекрывает оператор вызова (operator()), с тем же синтаксисом. И опять-таки ЕМНИП, его можно использовать везде, где используются функции - в STL, к примеру. И никакой модной сейчас мультипарадигменности в сторону ФП в этом не видно: это было ещё 1998/2003 (я более поздними стандартами не пользовался никогда).

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

Кажется, умные люди в умных ООП-книгах называют это «стратегией»

Если вы про шаблон Стратегия, то он про другое - про вынос во внешний объект логическисогласованного набора методов.

вместо простого
compose s1 s2 = \arg → s2 (s1 arg)

Ну, в C# с нынешним синтаксическим сахаром можно написать примерно так же:
static Func<T,T> Compose<T> (Func<T,T> s1, Func<T,T> s2) => arg => s2(s1(arg));

Раньше да, возни было больше: изволь определить тип, который хранит параметры-исходные делегаты, с конструктором и методом (а потому анонимный тип тут не подойдет), а потом создай экземпляр и верни его метод в качестве делегата. Но по смыслу и то, и то - одно и тоже, и промежуточнй код после компиляции - один и тот же, с точностью до имен типов или методов.

Можно в лямбду по значению захватывать, но да.

Вопрос тут, в какой памяти эти значения хранить.

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

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

"Не функция" - это уже детали реализации.

Так про них-то и речь. Концепт один и тот же: сделать из двух действий третье. Просто в классическом ООП реализация такая, что за деревьями теряется лес.

Если вы про шаблон Стратегия, то он про другое - про вынос во внешний объект логическисогласованного набора методов.

Не обязательно набора. Там и один метод выносится, и этот метод обзывается «точкой конфигурации» или чем-то таким. Только смысл у него простой: он, собсна, выражает действие.

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

Я ни разу не мыслил малыми категориями и морфизмами, и ими там мыслить совершенно не нужно.

Я мыслю операциями «применить к каждому узлу дерева такую-то функцию и собрать результаты в новое дерево» (это traverse), а затем, если надо, «собрать все эффекты из узлов нового дерева» (это sequence). Никакого теорката, ничего.

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

Тогда вам тот же вопрос, что и исходному товарищу. Что лучше,

int sum = 0;
for (int i = 0; i &lt; vec.size(); ++i)
  sum += vec[i];

или

const int sum = accumulate(vec, 0);

?

Только смысл у него простой: он, собсна, выражает действие.

И чо? Вполне себе понятная вещь: код, который будет выполнен в будущем. Если чо, я о таком выше писал. ФП тут не нужно - идее обработчиков уже сто лет в обед. Взять те же обработчики прерываний в какой-нибудь древней архитектуре, типа ЕС ЭВМ (в девичестве IBM System 360/370). Там эти самые обработчики изначально были. На ассемблере, естественно.

Я ни разу не мыслил малыми категориями и морфизмами, и ими там мыслить совершенно не нужно.

Ну, на самом деле, имелись в виду просто множества - входных параметров и результатов. Множествами тоже мыслить непросто, кстати, привычка нужна, хотя их и в школе проходят (ну, я точно проходил).

А вы, как я понял, мыслите действиями. Так вот, что, в вашем понимании, есть действие? Я этот вопрос, кстати, уже задавал. А уже тогда можно поговоримть об этом вашем концепте:

Концепт один и тот же: сделать из двух действий третье.

В частности - о его соответствии принципу YAGNI (который "А Скрипач не нужен, родной." (Скрипач-то, кстати, как раз был нужен - гравицаппу-то имено он свистнул!): может, проще захардкодить вызов этих двух функций в отдельном методе, и передавать его делегат (как я писал раньше, это - вполне в работу с ООП укладывается: там никто не запрещает заранее запрограммировать то, что будет вызвано потом). И обойдемся безо всякой композиции. Иногда я бы предпочел именно так: разработчики ASP.NET Core, например, вызывают метод построения конвейера обработчиков веб-приложения через довольно длинную и разбросанную по исходникам композицию делегатов - а в самом начале у них есть (по крайней мере была, в 3.1, который я у себя в статье разбирал, но подробностей уже не помню) ссылка на некий кэш в каком-то объекте, который они где-то втихаря подменяют. Пока читал и думал, разбирался как оно работает - мозоли на думалке натер.

Тогда вам тот же вопрос, что и исходному товарищу. Что лучше,

Тут хочется ответить по-еврейски: вопросом на вопрос. Точнее, несколькими. Написан ли уже этот accumulate? И если да, помнит ли про него программист, или ему надо в документацию лезть? И сколько таких мест, где такое нужно сделать? Ведь если их хотя бы с десяток, то можно accumulate и самому написать, это окупится. Короче, истина конкретна.

А если этими вопросами не задаваться, то и то, и то - одно и то же: O(N).

Вполне себе понятная вещь: код, который будет выполнен в будущем.

Это просто код. Добавка «который выполнен в будущем» либо требует уточнений, либо не имеет смысла (потому что не несёт никакой дополнительной информации — абсолютно любой с код с тем же успехом может быть выполнен в будущем).

ФП тут не нужно - идее обработчиков уже сто лет в обед.

ФП это делает юзабельным.

«Классическое» ФП а-ля нетипизированный лисп — потому что обработчики можно композировать. Сишные указатели на функции, как мы с вами оба согласились, композировать нельзя.

«Современное» ФП а-ля обмазали системой типов посильнее — потому что о коде после этого легче рассуждать. И вам не нужно делать interface ITextPredicate вместе с ITextOr и ITextAnd, вы можете просто написать string → bool.

Ну, на самом деле, имелись в виду просто множества - входных параметров и результатов. Множествами тоже мыслить непросто, кстати, привычка нужна, хотя их и в школе проходят (ну, я точно проходил).

Я и множествами не мыслю. Мыслю максимум типами, но почти синтаксически, точно так же, как работает proof finding в некоторых языках: условно, если у меня есть функция типа a → b и другая функция типа b → c, а мне надо a → c, то очевидно, как их совместить. Даже думать не надо, ни о морфизмах, ни о категориях, ни о множествах.

А вы, как я понял, мыслите действиями. Так вот, что, в вашем понимании, есть действие?

В моём — любой терм, тип которого имеет стрелочку первым конструктором типов. Ну, короче, что-то вида a → b.

может, проще захардкодить вызов этих двух функций в отдельном методе, и передавать его делегат

Кому проще и почему?

Вы там дальше про ASP.NET пишете, но у меня с ним ровно ноль опыта. В моём же опыте это оправдано тогда, когда одна и та же последовательность действий повторяется часто. Правда, тогда весь хардкод сводится к

compositeAction = f2 . f1

Написан ли уже этот accumulate?

Да. Это почти std::accumulate (только последний принимает пару итераторов вместо одного рейнджа, а std::ranges::accumulate почему-то не сделали, но для дискуссии это несущественно).

И если да, помнит ли про него программист, или ему надо в документацию лезть?

А вот это косвенно мой вам вопрос :]

Есть философия, что надо пользоваться стдлибкой по максимуму. Если вы придёте на конференцию по плюсам или почитаете модный блог, то там вам расскажут про std::for_each, std::accumulate, std::inner_product какой-нибудь, и так далее, и скажут, что их надо предпочитать голым циклам (потому что они делают код выразительнее, интент — понятнее, реализацию — безопаснее, и так далее).

А есть философия, конечно, что это всё бесовское.

И сколько таких мест, где такое нужно сделать?

Вы читаете код. Оба варианта уже написаны.

Это просто код. Добавка «который выполнен в будущем» либо требует уточнений, либо не имеет смысла (потому что не несёт никакой дополнительной информации — абсолютно любой с код с тем же успехом может быть выполнен в будущем).

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

ФП это делает юзабельным.

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

Сишные указатели на функции, как мы с вами оба согласились, композировать нельзя.

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

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

Я и множествами не мыслю.
В моём — любой терм, тип которого имеет стрелочку первым конструктором типов. Ну, короче, что-то вида a → b.

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

Кому проще и почему?

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

А вот это косвенно мой вам вопрос :]

Есть философия, что надо пользоваться стдлибкой по максимуму.

Я на этот вопрос уже отвечал, только в другой статье, про нововведения в C#, которую вы вряд ли читали (ибо вам она незачем): я предпочитаю, чтобы в языке был минимум синтаксических конструкций. Потому что каждая конструкция - это отдельная, нередко - разнородная сущность, а мозгу сложно оперировать одновременно с большим количеством таких сущностей. Ограничение там даже для тренированных людей весьма невелико - если сущности совсем уж разнородны, то чуть больше десятка. Синтаксические элементы не совсем разнородны, их можно группировать и держать в голове больше.

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

А те два варианта, которые вы писали, для меня почти одинаковы (почти - потому что надо в тело цикла для порядка все же посмотреть): весь этот цикл я воспринимаю как одну идиому, это, с одной стороны, конечно, чуть дольше (кое-что проверять надо, не накосячено ли с границами, к примеру), зато надежнее тем, и я всегда способен в этом убедиться. А то я не во всех интересующих меня языках аналогичные методы назубок помню: к примеру про reduce в JS для массива я точно помню, разве что, его название - а JS мне иногда нужен, чтобы тестовый фронт для моего бэка себе сделать.
Эти варианты для меня примерно так же одинаковы, как swap (ref a, ref b) и a^=b; b^=a; a^=b, где a и b - целые - этот трюк я ещё с советских времен освоил. Так вот, вернемся к циклу. Конечно, когда такой цикл пишут в несколько строчек (потому что сейчас так положено), это несколько мешает - выталкивает другие строки из области обозрения на экране. Но можно его так не писать и я,когда в свободном режиме всяечкие идиоматические вещи не стесняюсь писать в одну строку (как обмен двух целых выше написан).

Вы читаете код. Оба варианта уже написаны.

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

"Который запланирован к выполнению при определенных условиях" - достаточное уточнение?

Так это снова к любому коду применимо.

В Delphi обработчики событий были более чем годными к применению безо всякого ФП.

Как там будет выглядеть «взять переданный обработчик событий, навесить на него логгирование, и передать дальше»? Или «взять эти два обработчика событий и собрать из них третий, который выполняет первый, а затем, если первый вернул false, выполняет второй»?

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

Да, и в классическом ООП (без ФП-элементов вроде лямбд, которые не зря называются ФП-элементами) устраивается закат солнца руками: все эти объекты и экземпляры создаются программистом вручную, явно (что, кстати, даёт компилятору меньше простора для оптимизаций, но то другой разговор).

Вы и в сишечке можете сделать эмуляцию ООП (gtk передаёт привет), но это не делает C ООП-языком, или структуры + указатели на функции — классами.

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

Не, ИМХО это наоборот: так как без них получается громоздко, то техники не получают распространения. А потом приходят разработчики языка и добавляют всякие там лямбды, однострочные функции и прочую ерунду, и применений становится больше.

Тогда придется переформулировать вопрос - а что есть стрелочка?

Я могу дать вам несколько разных определений, от чисто синтаксических до всяких там денотаций и семантик, но они здесь не нужны. Достаточно интуитивного понимания «штука, которая принимает a, пыхтит и выдаёт b». Точно так же, к слову, я смогу задать последующие уточняющие вопросы к вашему определению классов и объектов, скажем, но это не значит, что их необходимо задавать (или понимать на них ответы) для успешного программирования.

я предпочитаю, чтобы в языке был минимум синтаксических конструкций

А это не синтаксис, это стандартная функция.

весь этот цикл я воспринимаю как одну идиому, это, с одной стороны, конечно, чуть дольше (кое-что проверять надо, не накосячено ли с границами, к примеру)

После того, как вы прочитали и убедились, что именно он делает.

Но, впрочем, окей, спасибо, ответ я получил. Здесь только могу сказать, что для меня ответ другой, и читать вариант с accumulate мне, конечно, проще (как и моим чисто плюсатым коллегам, почему-то так вот выходит).

Может и был бы проще accumulate, если бы назывался sum и не принимал ничего кроме массива.

Так это снова к любому коду применимо.

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

Как там будет выглядеть «взять переданный обработчик событий, навесить на него логгирование, и передать дальше»?

См. шаблон "Декоратор". Там всё написано.

все эти объекты и экземпляры создаются программистом вручную, явно

Ага. Потому что это - части от чуждой парадигмы. А в другой парадигме - ООП - например, они встречаются редко, потому что по жизни не нужны. Если только в создатели фреймворка, которым приходится пользоваться, не набегут любители чего-нибудь модного, типа того же ФП, и не насуют туда своих любимых приколов. С ASP.NET Core случилось именно это. Вплоть до того, что там в коде была (и сейчас есть) совершенно дивная лямбда, больше 100 строк размером (150 ЕМНИП), правда она - часть того костыля, которым инициализацию приложения в шаблоне Web Host, приколотили к новому шаблону Generic Host. Сейчас, кстати, разработчики осознали и исправились - и теперь веб-приложения, начиная с 6.0, безо всех этих заморочек, типа Startup-класса и его методов, которые ищутся, типа, по неким соглашениям: программирование по соглашениям, типа как декларативное: тоже тема модная, а нам потом все эти ритуалы запоминай. Да ещё и часть чисто C# заморочек, типа метода Main с глаз долой убрали, и программуможно писать как на бейсике или фортране - без лишних предисловий. На самом деле, архитектура - все та же охапка классов из Generic Host - внутри осталась (ибо совместимость), но ее хорошенько так спрятали под капот. И ритуалы инициализации стали выглядеть, в основном, по ООПшному - как вызовы нормальных методов с нормальными параметрами, а не с некими делегатами, которые неведомо когда вызываются. Не везде, правда - в RateLimiting, например, конфигурация делегатом задается, но в RateLimiting вообще тихий ужас творится: писали его, вроде бы, в парадигме ООП (подозреваю, на этом манагеры настояли), но сами разработчики, похоже, ООП то ли не знали, то ли не любили: там некоторые части годятся в качестве примера, как не надо пользоваться ООП (допишу статью про это, она в плане - выложу, с моими пояснениями, можно будет полюбоваться, самому в код по уши не зарываясь, а пока поверьте мне на слово, ну или конкретные ссылки на код в GitHub могу дать).

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

Мне - нужны. Потому что я не понимаю точного смысла, скказанного вами. Домыслить могу, но это будут мои домыслы, а мне самому с собой дискутировать не интересно. Как я уже писал в комментариях к упомянутой статье про новый C#, интуитивного понятия (с домыслами) мне для счастья недостаточно.

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

Основу - инкапсуляцию, наследование и полиморфизм - желательно знать и уметь применять. Иначе, без последних двух, получается царство копипасты, как в классе RateLimiter и его наследниках (из пакета Microsoft.Extensions.RateLimiting, я совсем недавно с ним подробно разбирался) - там в каждом из классов одинаковые методы реализованы независимо, схожим кодом с незначительными изменениями (которые я в WinDiff отлавливал): в наследование и полиморфизм разработчики явно не умели, или сочли их идеологически чуждыми - буржуазными, наверное :-)

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

У разработчиков есть другая нужда добавлять весь этот синтаксический сахар - маркетингу надо, потому что язык который "не развивается" плохо продается. История с PL/1 -"универсальным языком программирования", который не пережил мейнфреймы IBM - похоже, уже забылась. Или менеджменту она пофиг - язык рухнет под своей тяжестью когда-нибудь потом, а KPI и бонусы - они сейчас.

Но прежде чем пользоваться, надо всё это освоить - по котрому поводу я в комментариях к упомянутой статье про новый C# немало поворчал.

А это не синтаксис, это стандартная функция.

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

После того, как вы прочитали и убедились, что именно он делает.

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

Вы попробуйте что-нибудь на не слишком знакомом вам языке почтитать - на JS, к примеру, или на C# с LINQ (аналог map как там называется Select, а как называется аналог reduce, я, например, прямо сейчас не помню.

И да, у ФП есть ещё пара проблем, которые в в этом подходе сами не решаются. Во-первых, чистых функций в реальном мире недостаточно - потому что ввод-вывод, сеть, БД. И решение с "моноидами в категории эндофункторов" - оно как-то не выглядит интуитивно понятным широким массам (впрочем, есть более человекочитаемые описания, в том числе и на Хабре, что это за хрень - монада, и как с ней бороться). А во-вторых, я не вижу средств снижения потенциальной сложности программ за счет ограничения области, с которой имеешь дело. А объекты - они как раз для этого придуманы, чтобы избегать в программе случайных связей (имеется в виду - между частями программы, а не между программистами и программистками). Модули-то там у вас хоть есть?

Или вы уже настолько погрузились в это свое ФП, что для вас порядок выполнения не существует?

По-хорошему (в денотационной семантике), конечно, порядок выполнения не существует. А вы так пишете, как будто в этом есть что-то плохое.

Да, я знаю, что в ФП порядок выполнения не существует, там sqrt(9) и 3 суть одно и тоже. А вот по жизни порядок выполнения очень даже существует: сначала на фронте пользователь жмет кнопку, а уже потом интерфейс в ответ на это перересовывается, сначала на бэк приходит запрос от фронта, потом бэк выдает ему результат и так далее.

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

То, что вы написали - следствие не порядка выполнения, а просто функциональной зависимости результата от исходных данных. Как раз для бэка это обстоятельство очень важно, потому что засинхронизировать конкурентные запросы по порядку исполнения обычно невозможно или нежелательно.

Собственно, микросервисы - это и есть функции.

То, что вы написали - следствие не порядка выполнения, а просто функциональной зависимости результата от исходных данных.

Ну да, причинность пока что не отменили (хотя физики пытаются). Но по жизни в исходные данные попадает и предыстория, и возникшее в результате этой предыстории текущее состояние системы. Не верите? Тогда попробуйте реализовать чисто в функциональном стиле, без понятия состояния, такую вполне практичную вещь, как ограничение скорости обработки запросов - Rate Limiting.

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

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

Собственно, микросервисы - это и есть функции.

Некоторые микросервисы. Те, у которых нет своего состояния (БД, к примеру). Ну, и чтобы они не зависили от какого-то внешнего состояния: микросервис для текущей погоды например - это не функция.

Короче, ФП местами полезно, но применимость его ограничена. Не надо выдавать его за универсальный подход.

Тогда попробуйте реализовать чисто в функциональном стиле, без понятия состояния, такую вполне практичную вещь, как ограничение скорости обработки запросов - Rate Limiting.

А зачем для этого понятие состояния? Просто ленивое вычисление функций с задержкой по таймеру, например. Или мейлбокс. Или любая другая абстракция очереди. Я не то, что "пробую", я именно этим и занимаюсь.

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

  1. Для порядка, это не финансы, а продажи.

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

Некоторые микросервисы. Те, у которых нет своего состояния (БД, к примеру). Ну, и чтобы они не зависили от какого-то внешнего состояния: микросервис для текущей погоды например - это не функция.

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

Если совсем уж зарубиться в теорию, то вот в чём заключается всё (бестиповое) функциональное программирование:

ε включает окружение
ε включает окружение

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

А зачем для этого понятие состояния? Просто ленивое вычисление функций с задержкой по таймеру, например. Или мейлбокс. Или любая другая абстракция очереди. Я не то, что "пробую", я именно этим и занимаюсь.

По-моему, вы тут занимаетесь тем, что хотите вместо решения задчи, которую я прошу решить, подпихнуть решение совсем другой задачи: всего лишь превращением неравномерного потока запросов в равномерный, без отбрасывания каких-либо излишних запросов (аналог Shaping на жаргоне для DiffServ QoS сетевиков). Задача Rate Limiting в реальности этим не исчерпывается, нужен ещё и Policing на том же жаргоне: получение разрешения на выполнение запроса может не отложить его выполнение в очереди и т.п., а просто вернуть отказ, в зависимости от состояния (истории запросов, например). Так что получение разрешения - это никак не функция - если, конечно, никак не хранить это состяние где-то снаружи и не подавать каждый раз на вход этой функции(а в таком варианте ФП для полного решения задачи недостаточно, и не факт, что от него вообще практическая польза будет).
И я не понимаю, как очередь или mailbox можно реализовать программированием через функции, то есть, без хранения состояния - когда состояние там явно есть, чисто по жизни. Ну и, чистый Shaping - он негибкий, иногда полезно начальные всплески отрабатывать отдельно, и для этого есть более другие алгоритмы - Token Bucket, например, или оконные - а там без состояния не обойтись.
А потому, на мой взгляд, ФП не может служить средством для создания полного решения задач Rate Limiting по указанным выше причинам.

Для порядка, это не финансы, а продажи.

Если вам так нужен порядок, читайте вместо слова "финансовый" выражение "гле про деньги" - я именно этот смычл имел в виду.

Это не порядок исполнения, а функциональная зависимость.

От чего зависимость-то? Что является входными параметрами?

вам в императивном коде приходится рассматривать предоплату и кредит (обещание будущей оплаты) как разные случаи,

На это есть средство: интерфейсы. Они даже не совсем ООП - по крайней мере, про наследование с полиморфизмом там знать не надо, реализовывать можно через композицию/агрегирование (что имеет плюс - вызывает у простого народа куда меньше непоняток и хейта, чем наследование с полиморфизмом), интерфейсы можно использовать хоть на C: в Visual Studio (в 6.0 по крайней мере) есть заголовочные файлы для COM-интерфейсов для программ на чистом C без единого плюса. Так что ФП тут не нужно.

Содержимое БД и текущая погода не являются состояниями микросервиса.

Про погоду не скажу, а вот содержимое БД - это именно состояние. Особенно, если БД у сервиса собственная.

Если совсем уж зарубиться в теорию

Там для слова "meaing" где-то раньше должно быть определение, что оно значит (извините за невольный каламбур). Без этого мне просто непонятно, что хотел сказать автор отрывка, а потому его ценность от меня ускользает.

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

Ну, теорию я не люблю любую, так что меня это не парит (Эд Пост писал: "настоящему программисту не нужны абстрактные концепции, чтобы делать конкретную работу" и тут я с ним полностью согласен). Но - не факт что нарушает: инкапсуляция - она для программы с кодом, а не для пассивных данных, данные сами себя не поменяют внезапно.

Задача Rate Limiting в реальности этим не исчерпывается, нужен ещё и Policing на том же жаргоне: получение разрешения на выполнение запроса может не отложить его выполнение в очереди и т.п., а просто вернуть отказ, в зависимости от состояния (истории запросов, например). Так что получение разрешения - это никак не функция - если, конечно, никак не хранить это состяние где-то снаружи и не подавать каждый раз на вход этой функции(а в таком варианте ФП для полного решения задачи недостаточно, и не факт, что от него вообще практическая польза будет).

Если вам кто-то сказал, что в ФП нельзя использовать состояние, или что любая информация в ФП передаётся через параметры, то этот человек вас подло обманул. Я выше написал (может быть, недостаточно явно), что состояние окружения является центральным понятием ФП. Как минимум, сами тела функций в любом случае ведь откуда-то берутся, и они как раз являются состояниями окружения.

Поэтому спор по данному направлению бессодержателен, так как происходит из неверных предпосылок.

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

На самом деле я говорю вот о чём.

Рассмотрим, например, простейший код:

double a[N];
for (i=0; i<N; i++)
  a[i]=0.0;

Здесь в силу выразительных особенностей императивного языка подразумевается определённая последовательность обнуления элементов массива, от начала к концу (операционная семантика). Однако денотационно это означает просто "обнулить массив", и какая-либо последовательность тут нафиг не упала. Поэтому, между прочим, современный стандарт C++ уточняет, что, хоть мы и пишем здесь в операционной семантике последовательного цикла, но дальше мы в эту семантику заворачиваем рыбу, и компилятор вправе от неё отказаться. В каком-нибудь языке повыразительнее мы бы написали просто

a = 0.0

не занимаясь придумыванием ненужной и даже вредной последовательности действий.

И если хорошенько задуматься, то большинство последовательностей действий в программах являются именно такими, произвольными, ничего не выражающими содержательно. Многие операторы можно менять местами без изменения результата (чем, в частности, иногда занимаются оптимизирующие компиляторы). Многое можно было бы распараллелить. И всё это во многом является созданием проблем на ровном месте, просто из-за того, что когда-то школьнику сказали, что алгоритм – это последовательность шагов.

От чего зависимость-то? Что является входными параметрами?

Булевское условие оплаты, очевидно.

Про погоду не скажу, а вот содержимое БД - это именно состояние. Особенно, если БД у сервиса собственная.

Вы много видели микросервисов с собственной БД?

Там для слова "meaing" где-то раньше должно быть определение, что оно значит (извините за невольный каламбур). Без этого мне просто непонятно, что хотел сказать автор отрывка, а потому его ценность от меня ускользает.

Для простоты можно сказать, что meaning означает преобразование из синтаксиса в семантику (т.е. морфизм из множества синтаксических конструкций в множество семантических). Конкретно у авторов (а это цитата из стандарта языка Scheme) приведено определение семантической функции в формальной семантике (раздел 7.2.3), но оно слишком сложное и специфическое для нашего обсуждения.

А ценность отрывка в том, что он показывает, что ФП – это просто последовательно применённый формальный способ вычисления семантики по синтаксису, а не то, что ему часто ошибочно приписывают.

инкапсуляция - она для программы с кодом, а не для пассивных данных, данные сами себя не поменяют внезапно.

  1. Я не вижу разницы между кодом и данными.

  2. Вроде именно ООП настаивает на том, что не должно быть пассивных данных. Почему общие статические переменные в программе – это плохо, а база данных – хорошо? Всюду двоемыслие.

Я выше написал (может быть, недостаточно явно), что состояние окружения является центральным понятием ФП.

Тогда, наверное, мы говорим о чем-то разном. Я говорил о программировании с помощью функций, как про это рассказано в первых главах старой (1987, русский перевод - 1993) книжке Филда и Харрисона "Функциональное программирование". Именно такой подход страдает той самой неполнотой.

Поэтому спор по данному направлению бессодержателен, так как происходит из неверных предпосылок.

Чтобы придать содержательность, ответьте на пару вопросов:

  1. Является ли состояния окружения изменякмым?

  2. Может ли программа изменять состояние своего окруженния?

Если оба ответа - "да", то в чем тогда состоит функциональность ФП? И, в частности, по второму вопросу: надо ли отделять сотояние окружения от состояния программы - где польза в таком разделении? Может, в таком случае лучше говорить о "реактивном программировании" (тут на Хабре, кстати, совсем недавно была очередная статья - нынтье на тему React).

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

Не требует. Но концептуально они близки: "если очередной запрос укладывается в заданные ограничения - выполнить, инче - сделать с ним что-нибудь". Это самое "что-нибудь" может включать в себя "отклонить и сообщить/не сообщать инициатору", "поставить в очередь", "поставить в очередь, если она не слишком велика, иначе отклонить с/без сообщения инициатору" - т.е. варианов немного. Отсюда и структура этого куска программы: проверить ограничения, отправить на выполнение/отклонить (с без уведомления инициатора), скорректировать ограничения, скорректировать, если надо, статистику/метрики. Значительная часть этого набора операций - общая, а потому разумно сделать универсальный код. Впрочем, разработчики RateLimiting для .NET этим не озаботились, и там всё плохо - у них там свой код под каждый алгоритм ограничения с кучей дубляжа в разных модулях - но могло быть и хуже. Доберусь - может, статью даже напишу, на тему как не надо использовать ООП, с примерами оттуда, но этоуже совсем другая история, не про шейпинг/полисинг.

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

Это - идиома, она воспринимается как единая операция: и при чтении человеком - я выше про это про себя писал, и при компиляции - оптимизирующий компилятор запросто сможет заменить это одной инструкцией, типа REP STOSW, если целевая архитектура - x86. Конкретная последовательность там нафиг никому не сдалась - как никого не интересует, в каком порядке некто считает ворон, и даже - что такое баклуши, которые он бьет.

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

Даже если большинство - то отнюдь не все. И могут быть нюансы: например, процессор организует спекулятивное выполнение инструкций - и от этого получается уязвимость Meltdown/Spectre, и можно, например, в результате почитать память, которую читать не положено.

И всё это во многом является созданием проблем на ровном месте, просто из-за того, что когда-то школьнику сказали, что алгоритм – это последовательность шагов.

Не на ровном месте. В реальном мире есть, к примеру, зависимость по данным. Но тут опять всё приходит ко второму вопросу, про наличие изменяемого состояния: есть оно в парадигме - зависимость по данным возможна, как и конфликты обновления состояния. Потому так и сладки теоретикам чистые функции, где этой зависимости нет. Но реальный мир устроен сложнее, неудобно, часто эта зависимость объективно есть, а потому применимость ФП к реальному миру ограничена.

Ну, и частностию

Булевское условие оплаты, очевидно.

Не очевидно: деньги счет любят, так что нужна конкретная сумма.

Вы много видели микросервисов с собственной БД?

Я вообще видел мало микросервисов, примерно как чёрт ладан мало нюхал, и примерно по той же причине. Но я про них много слышал/читал, в т.ч. - здесь, на Хабре: мир не из одних лямбд в облаках состоит.

А ценность отрывка в том, что он показывает, что ФП – это просто последовательно применённый формальный способ вычисления семантики по синтаксису, а не то, что ему часто ошибочно приписывают.

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

Я не вижу разницы между кодом и данными.

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

Вроде именно ООП настаивает на том, что не должно быть пассивных данных.

Это DDD настаивает. А DDD - это далеко не все ООП (и, к слову, не меньшая религия, чем ФП, так что я за нее мазу тянуть не буду).

Почему общие статические переменные в программе – это плохо, а база данных – хорошо?

Не знаю. IMHO - проявление религии, Credo quia absurdum. Я лично таких странных вещей не писал.

Всюду двоемыслие.

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

Я говорил о программировании с помощью функций, как про это рассказано в первых главах старой (1987, русский перевод - 1993) книжке Филда и Харрисона "Функциональное программирование".

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

Чтобы придать содержательность, ответьте на пару вопросов:

  1. Является ли состояния окружения изменякмым?

  2. Может ли программа изменять состояние своего окруженния?

Является и может, конечно. Простейшим способом изменения окружения является само по себе применение функции (в просторечии – функциональный вызов). Когда мы пишем:

((lambda (x) expr) 1)

то здесь в окружении выражения expr значение символа x меняется на 1. Это и есть самая основа лямбда-исчисления, бета-редукция (с несущественной для нас точностью до порядка вычислений).

Тождественно то же самое (немедленное применение лямбда-выражения) для удобочитаемости можно записать в форме:

(let
 ((x 1))
  expr)

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

Другое дело, что в ФП не принято злоупотреблять изменением нелокального окружения, то есть, в частности, неограниченно длящимся присваиванием. Хотя само по себе определение функции как раз и является примером изменения внешнего окружения – присваиванием имени функции в (чаще всего) глобальном контексте значения, представляющего собой её тело:

(define (f) expr)

то же самое, что

(define f (lambda () expr))

или

(define f)
(set! f (lambda () expr))

то есть самое настоящее определение переменной и присваивание значения.

Если оба ответа - "да", то в чем тогда состоит функциональность ФП?

В том, что связь исходных данных и результата (т.е. программа) представляется как суперпозиция применения функциональных форм.

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

Состояние окружения – это формально определённый теоретический термин, а состояние программы – практический программистский. Разница между ними всё же есть, потому что к состоянию окружения относятся также данные, внешние по отношению к программе – содержимое файлов, состояние внешних устройств, действия пользователя и т.п.

Может, в таком случае лучше говорить о "реактивном программировании" (тут на Хабре, кстати, совсем недавно была очередная статья - нынтье на тему React).

Я не специалист в области реактивного программирования, поэтому могу что-то изложить неверно. В моём понимании, реактивное программирование – это парадигма операционной семантики, то есть способа выполнения программы. В своей основе реактивное программирование запросто может опираться на функциональное программирование, как парадигму денотационной сематики (т.е. описания связи между входом и выходом). Само по себе ФП не подразумевает какую-то конкретную последовательность действий.

потому, что ФП реально накладывает чрезмерные ограничения на допустимый синтаксис: например, в нем AFAIK концептуально нет переменных, хранящих состояние программы, и нет оператора присвоения значения таким переменным. Так?

Тут мы увязаем в тонкостях формулировок. Смотря что вы понимаете под переменными и оператором присваивания. В таком виде, как в императивном программировании, их нет (точнее, их не принято применять в функциональном стиле – примерно как с goto). Вместе с тем, чем вам описанная выше форма let хуже переменных с присваиванием? Скажу сразу, что ничем не хуже, так как именно лямбда-исчисление является теоретическим фундаментом любого программирования, в том числе и императивного.

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

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

Впрочем, двоемыслие - это форма диалектики, но эта тема уже далеко выходит за пределы дискуссии о ФП.

А я и не стал бы спорить с этим утверждением.

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

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

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

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

выполнять параллельно с уверенностью в заведомой невозможности возникновения конфликтов между параллельно выполняемыми экземплярами

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

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

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

ФП в вашем изложении начинает выглядить настолько всеобъемлюще,

Оно и есть всеобъемлющее. Это теоретический фундамент программирования вообще.

что в целом становится неотличиным от императивного программирования.

Ну как неотличимым? По коду сразу видно. А результат, конечно, тот же самый.

В конечном итоге компьютер делает только одно - заменяет одни значения битов другими по таблице. А императивное и функциональное программирование - просто семантические абстракции, то есть наш способ упрощённо думать об этом процессе, не запоминая огромных таблиц подстановки битов. Исходя из этого, разницы нет. Но в удобстве для человека в тех или иных случаях разница есть.

Но в удобстве для человека в тех или иных случаях разница есть.

Удобство - понятие субъективное. Например, мне вот лично удобно мыслить иерархиями классов ООП, но здесь на Хабре есть некоторое количество людей, которым этот подход претит настолько, что они статьи пишут.

И лично мне программировать функциями неудобно.

Я ещё подумал и сказал бы так: использование парочки лямбд не делает императивную программу функциональной, как и использование парочки операторов присваивания не делает функциональную программу императивной. Идеальная чистота парадигмы уместна только в теоретических рассуждениях; практика – путь компромиссов.

Я сейчас взял реальную коммерческую программу, написанную на языке Scheme в парадигме ФП. Она содержит 4316 строк, из них 69 форм присваивания (не считая 240 определений функций, которые формально тоже являются присваиваниями по объяснённой выше причине). Это явно больше нуля, но явно меньше того, что получилось бы в императивной программе.

Я посмотрел вскользь книжку Филда и Харрисона. Как я и подозревал, они просто излагают невнятно. Когда они пишут, что функции не могут изменить среду вычислений, то подразумевают при этом внешнюю для конкретной функции среду, то есть отсутствие побочных эффектов. Это никак не мешает функции менять среду внутри себя, что они и иллюстрируют оператором letсвоего языка Hope.

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

А это и есть псевдоним правой части, коль скоро нет побочных эффектов. Имя является псевдонимом значения, а значение является псевдонимом способа своего вычисления.

Решение на C# для следующего флейма.
Я второй раз в жизни вижу C# (серьезно!), но на таком уровне, наверное, любой сможет.

Скрытый текст
public static Either<List<Exception>, Node<Either<Exception, string>>>
  TryLoadComments(Node<int> tree)
{
  using HttpClient client = new();

  /* traverse tree */
  var IdToResult = (int x) =>
  {
    var task = TryLoadCommentAsync(client, x);
    return task.Result;
  };
  var commentTree = Node.Traverse(tree, IdToResult);

  /* exceptions */
  List<Exception> errList = new();
  var CollectExceptions = (Either<Exception, string> val, List<Exception> result) =>
  {
    if (val.IsLeft)
      result.Add((Exception)val); /* really */
  };

  Node.ForEach(commentTree, CollectExceptions, errList);

  if (errList.Length() > 0)
    return errList;

  return commentTree;
}

Полностью: https://pastebin.com/LbJcurE3
Алиасы типов в C# есть, но у меня это не компилировалось, проблема в руках.

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

Эти исследования либо некорректны, либо врут, либо не существуют. В больших проектах код приходится чаще читать, чем писать. Буквально, на одну написанную строчку приходятся десятки, а то и сотни прочитанных строк. И чем больше проект - тем больше нужно читать, прежде чем что-то дописывать. А вот тут как раз все языки разные. У парадигм разные уровни абстракции.

Если брать целиком цикл "прочитать, подумать, дописать, отладить, написать тесты", то ни о каком равенстве речи быть не может. Тут даже c# и c# - это две большие разницы. ООП даёт инструменты для работы на верхних уровнях логики, чтобы не было необходимости читать каждую реализацию. Но это только инструменты, они сами по себе не работают. Нужно ими пользоваться.

Странно, недавно читал про речь - выяснили, что информация передается с одной скоростью в единицу времени. В языках, где больше букв/слов нужно просто повышается скорость речи.
То что в коде это не так - довольно удивительно. Особенно с учетом того, что очевидно, что за 8 часов набора кода строк можно набрать на порядок больше, чем уйдет в гит по итогам дня, то есть скорость набора это не ограничение.

Именно так. Нужно каждый раз собирать решение под задачу, если только задача не является предельно типовой, под которую такое решение уже есть.

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

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

А вот про ООП мне есть что сказать. Я за последние несколько леи поработал на разных проектах с разными командами, и вижу, что люди массово не умеют в ООП. Умные слова типа "интерфейс", "SOLID", "инкапсуляция" и другие они выучили, и считают, что теперь они пишут ООП код. А они нет. Городят абстракции где не надо, где надо не делают, интерфейсы у них по 10 методов, в классах по 20 филдов, DTO мутабельные, одни приватные методы вызывают другие приватные методы матрёшкой, все это обмазано switch/case и instanceof, try-catch напоминает местами простыню тайпчеков по разным ексепшенам, и конца и края этому нет. Проблема не в ООП как инструменте, а в том, что многие не умеют инструментом пользоваться, и это грустно

А null? Там же были проверки на нул в каждом методе? :)

У меня ровно такая же проблема с командой. Только теперь я могу на это повлиять.

Повсеместного null-чекинга не помню, но не исключаю, что моя психика в какой-то момент просто уже не выдержала и заблокировала травму :(

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

Как говорил Фаулер: "if you can't change your team - change your team" :)

Правильно сделали. Нечего в чужом болоте тонуть.

Умные слова типа "интерфейс", "SOLID", "инкапсуляция" и другие они выучили, и считают, что теперь они пишут ООП код. А они нет. Городят абстракции где не надо, где надо не делают, интерфейсы у них по 10 методов, в классах по 20 филдов, DTO мутабельные, одни приватные методы вызывают другие приватные методы матрёшкой, все это обмазано switch/case и instanceof, try-catch напоминает местами простыню тайпчеков по разным ексепшенам, и конца и края этому нет.

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

У меня была несколько иная проблема, скорее, как раз нехватка деления на классы. Приложение было плюс-минус классическим CRUD-сервисом, в которое прилетали разные запросы типа "создать заявку", "изменить сущность" и так далее

И на это был один большой класс, который обрабатывал все возможные запросы. А поскольку запросов было много разных, то и класс был 1000+ строк кода, в нем было около 20 филдов-сервисов, внутренняя логика вся строилась на switch/case, и ОГРОМНОЕ количество приватных методов, которые вызывали друг друга матрешкой, а где-то очень глубоко внутри ещё и сайд-эффекты могли содержать, и без того, чтобы всю эту матрешку просмотреть, фиг ты найдёшь, что и где изменяется

А из ненужных и вредных абстракций там например какого-то хрена была сложная иерархия Entity-объектов, у которых id поля были обернуты во врапперы. Типа не просто UUID id, а CreateOrderId id, где CreateOrderId - это класс с одним полем внутри - тем самым UUID id. Полгода я просидел на том проекте и ни разу не увидел, чтобы это решение хоть где-то привело к чему-нибудь хорошему, а не лишним телодвижениям типа кастинга типов

При этом эти ребятки не любили lombok, поэтому вся эта чехарда была ещё и обильно обмазана бойлерплейтом

Попытки добавить ломбок были встречены "это как-то новая технология, мы ее не знаем, изучать будет долго, а ещё страшно, что она что-нибудь нам поломает", попытки классы разделить на несколько поменьше и сделать Стратегию "слишком сложно, паттерны не нужны, switch/case проще и понятнее", попытки добавлять к своим PR юнит тесты "юнит тесты слишком фиксируют внутреннюю логику, надо делать только интеграционные"

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

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

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

Однако в истоках их возникновения вы ошибаетесь. Они уходят корнями в "научный метод" и "производственную систему Toyota". В начале 90-ых (после выхода GoF) на конференциях для программистов начинают появляться "паттерны разработки". И в 2001 появляется Agile Manifesto, который разработали тоже, в основном, программисты.

Методологии нужны, чтобы хоть как-то понимать на каком этапе находится процесс. Чтобы менеджерское искусство превращать в ремесло

Извините меня конечно, но если вы занимаетесь просвещением людей по наболевшей теме, как можно писать... такое?

ООП отлично справляется с моделированием передачи данных, и никак не помогает в их трансформации. Если вам нужно превратить один тип данных в другой по каким то правилам - воспользуйтесь ФП или СП

Помните, что ООП - это про посылку сообщений.

Вы хотя-бы книгу Мартина читали про архитектуру? Или банды про ООП...

Поддерживаю.
На хабре в этой статье уже спорили на тему что такое ООП - https://habr.com/ru/articles/554474/
Мне ближе 3-6 парадигм ООП, чем просто одна из них - обмен сообщениями.
Походу это привычка сформированная обилием материала с этим уколоном.
Вероятно он победил, если более распространен, или я попал не в ту ветку, о боже какая катастрофа :)
Как и то что массивы индексируются, хотя индекса там нет, есть смещение. Видать холиварить тут не так интересно :)

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

Дядю Боба сразу фтопку, некомпетентный голый король. Достаточно почитать его "баттл" с Джоном Аустерхаутом.

Не совсем понял, что значит "писать такое". Но наверно вас смутило расхождение моей формулировки (а на самом деле формулировки Алана Кея) и Мартина + GoF.

Так как мы в этой статье возвращаемся к "истокам", то вот формулировка Алана Кея (инженера PARC, создавшего привычное нам ООП и Smalltalk):

Для меня ООП — это только отправка сообщений, локальное хранение, защита и сокрытие состояния-процесса, а также предельно позднее связывание всего

Так что даже прочитав все книги Роберта Мартина и GoF, я придерживаюсь изначального определения. А особенно в этой статье, которая говорит о возникновении и истоках ООП.

Вот почему я придерживаюсь именно определения с отправкой сообщений:

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

  2. Это простое определение. Не лёгкое, а именно простое. Это значит, что из него ничего нельзя выкинуть (оно имеет минимальное кол-во элементов), и при изменении любого элемента мы можем предсказать будущее поведение системы (все причина-следственные связи очевидны). Иметь 3-4 принципа (абстракция, инкапсуляция и т.д.) - это сложно, потому что они не связаны между собой

Так в цитате которую вы приводите написано хранение состояния. Объединение кода и данных и их сокрытие. И обмен сообщениями. Тоесть все парадигмы ООП и перечислены, ну кроме наследования. Вы просто видите то что вам удобно.

Да и в новых языках типа го или раст от ООП уходят.

В этом и заключается "простота" определения через "сообщения". Если у вас есть идея о посылке сообщений, то из неё можно вывести все остальные положения. Но вот из всех остальных положений, даже вместе, идея о сообщениях логически не следует. Можно придумать кучу других интерпретаций, в которых сообщений не будет.

Как из посылки сообщений наследование растет?

И тут я должен посмотреть хитрым взглядов и сказать, что оно и не растёт. Потом указать на то, что в определении Алана Кея нет ни слова про наследование...

Но вы же понимаете, какой холивар из этого начнётся?

Да просто спор о терминах. Вы сейчас косплеите слегка, на мой взгляд Чуковского, который очень не любил слово "наверное", точнее то, как поменялось его применение за жизнь писателя. То что означало уверенность в его детстве стало расплывчатым к его старости.

С ООП аналогично. Когда первый раз сказали - были сообщения между объектами. Когда я с этим в 98 году знакомился - это уже были однозначно данные + методы их обработки собранные в один объект. Ну и интерфейсы, полиморфизм, наследование и инкапсуляция вытекающие из этой идеи объединения данных и методов.

Иметь 3-4 принципа (абстракция, инкапсуляция и т.д.) - это сложно, потому что они не связаны между собой

Как это не связаны, если благодаря инкапсуляция достигается наследование. А благодаря инкапсуляции с наследованием уже и полиморфизм

Да, достигается, но не выводится. Иначе мы могли бы взять только что-то одно (например "Инкапсуляцию") и не упоминать всё остальное, потому что всё остальное было бы неизбежным следствием. Но мы так сделать не можем, потому что "наследование" никак не является неизбежным следствием "инкапсуляции".

Если в основе любой системы не лежит 1-2 принципа, из которых выводится всё остальное, то такая система сложна. И если подумать подольше, то её можно упростить. Это не легко, но так рождаются лучшие идеи

5 надо. 4 элементарных и один спорный. Так с древних греков повелось.

Забавно, потому что я тоже об этом думал. И эта ситуация лишь подтверждает, что упрощение рождает лучшие идеи.

Математики не принимали 5, потому что 5 - это много. И они приложили огромное кол-во усилий, чтобы сократить 5 до 4. А когда им это удалось - миру открылись 2 новых типа геометрии.

Если бы не желание учёных всё упрощать и уменьшать кол-во элементов, то может они и не открыли бы эти 2 новые геометрии

Для трансформации лучше всего подходит функциональная парадигма. Для хранения (чтение и запись) - реляционная.

Интересно было бы посмотреть на пример. Пока не очень понятно

Вот хороший и лёгкий пример применения ООП.

Нет, вы привели антипаттерн. В вашем примере нет понимания разницы между интерфейсом и конкретным классом. RTFM

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

Вот два примера «к несчастью»: MFC и jQuery. MFC создавалась как способ писать редакторы файлов на базе парадигмы Model-View-Controller. Но для 95% программистов это была либо «стандартная» библиотека C++ (тут я их не осуждаю, CString и CArray были очень дружелюбными по сравнению с другими стандартными библиотеками, с кавычками и без), либо тоненькая обёртка над WinAPI. jQuery была попыткой сделать более функциональной работу с DOM (язык JS был изрядно функциональным, а вот корявый DOM API браузеров — императивным на уровне, я не знаю, Си или ассемблера). Но для 95% программистов это был всего лишь селекторный движок. Когда в корявом DOM API браузеров появился доступ к нативному селекторному движку, для многих программистов jQuery стал не нужен.

Что касается ООП, тут дело сложилось более счастливым образом. Во-первых, если почитать Алана Кея, который, типа, изобрёл ООП, он же рассказывает, что сделал это, применив принцип Бартона. Отсюда все эти «объекты это маленькие компьютеры» и «отправки писем». Но принцип Бартона можно применить и иначе, как в C++: уравнять функции с полями и получить методы (ой, извините, член-функции… пока кто-нибудь не уронил монокль в бокал с коктейлем (ц)). А можно вообще вывести из принципа Бартона функциональное программирование. Например, уравнять функции с аргументами в праве быть параметрами.

Так что, не думаю, что правильно понимать идею ООП как «маленькие компьютеры» и «отправки писем». (Я бы даже сказал, это довольно неочевидное применение принципа Бартона). Самое продуктивное (приносящее огромную пользу) понимание, что я видел, это считать ООП способом организации кода, который поможет человеку, знающему предметную область, легче находить имплементации. Юзеру библиотеки — написать точку или стрелочку и увидеть полный список. А автору библиотеки найти класс, а затем искать имплементацию (структуру полей и функции) в нём.

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

Очень странный взгляд на вещи.

Что вы тогда скажете о DSL?

Или у вас "человек, знающий предметную область" одновременно автоматически является ООП-программистом? Тогда утверждение, что ООП помогает ООП-программисту, является тавтологией.

Этот «очень странный взгляд на вещи» распространён гораздо шире, чем исходная идея Алана Кея с «письмами». О чём, по-вашему, эта статья?

Апелляция к мнению большинства некорректна для объективно измеримых величин.

А что вы здесь назвали "объективно измеримой величиной", интересно? Определение и тем более понимание ООП - это по-моему одна из наиболее субъективных вещей в индустрии. Возьмите 30 программистов, дайте им сколь-нибудь нетривиальную задачу на ООА, и вы не увидите двух одинаковых решений.

способом организации кода, который поможет человеку, знающему предметную область, легче находить имплементации

Взять некоторое количество таких людей (большинство из которых, понятное дело, не будут программистами) и проверить их способность организовывать ООП код. Потом то же с DSL.

Возьмите непрограммистов, да возьмите даже программистов - вы не сможете объективно проверить их способность организовывать ООП код. Вы сможете высказать свое субъективное мнение об их способностях организовывать ООП код, Алан Кей сможет высказать свое субъективное мнение, Вася с горы тоже сможет высказать... Про какую "объективно измеримую величину" речь?

Способность писать код на языке программирования (неважно, хорошо или плохо), вполне измерима.

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

Я вообще не говорю про качество кода, тем более что это оценочное суждение.

Нет, распространен не этот, а три слова "инкапсуляция, полиморфизм, наследование".

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

Интересно, автор сам что-нибудь писал серьезное в ООП парадигме ? Ну не делаем мы геттеры и сеттеры на каждое поле. Зачем выставлять наружу внутренние кишки реализации алгоритма ? Да, бывают классы, предназначенные только для хранения данных, они называются структурами в C++ и POD в Java но у них не делают геттеры и сеттеры, они там не нужны. Просто все поля делают public.

Автор о том и пишет, что изначальный смысл потерялся. Изначальному ООП в стиле Смоллтока с геттерами и сеттерами было, например, наплевать, как распределены объекты внутри компьютера или по сети. А чтобы использовать public поля, вы должны быть, как минимум, в одном адресном пространстве с используемым объектом.

Классического ООП, честно говоря, больше осталось в функциональных языках вроде Erlang и Termite, чем в современных "объектно-ориентированных" языках.

Месяц на спринт в более-менее крупном продукте - это курам на смех, ни о чем вообще. Неделю просто скушает общение по фиче с заказчиком, еще две - постановка задач по тому, что узнали от заказчика, уточнения, еще к юристам сходить надо, чтобы вычитали. Сколько там разработка, учитывать не будем, ибо зависит от фичи. Дальше фичу надо отдать на тестирование, и тестируется не только фича, но и весь функционал, который потенциально может быть задет. Еще неделя на ручное тестирование, автотесты, проверки ИБ. Дальше всегда будут баги, которые надо закрыть - еще неделя-две с частичным перетестированием. И, может быть, неделю на релиз, потому как его надо согласовать с бизнесом - не у всех бесшовная раскатка есть. Цикл в три месяца - это минимум для крупного продукта. Для небольшого продукта и простых фич можно уложится в месяц, но это прям с авралом и горящими ср(а|о)ками.

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

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

Проверку юристов и безопасников как мне кажется можно делать параллельно с разработкой уже другого функционала.

А если выделить из всего этого только собственно проектную техническую активность?

Вот сначала (1) прошла разработка постановки, общение с заказчиком, юристами - выкидываем.

После прошло (2) тестирование, проверка ИБ, раскатка на прод, опытно-промышленная эксплуатация.

А вот между (1) и (2) как раз укладывается описанный в статье цикл. Когда в команду разработки приходит уже сформированная на уровне бизнеса постановка (но которую еще надо превратить в постановку техническую) и к концу месяца команда вкатывает на препрод некую функциональность ценную для бизнеса.

А пока идет 1 и 2 проектная команда делает какой-то другой кусок функционала.

При всем при том как по мне нет смысла прямо жестко подгонять действительность в какой-то один магический срок смысла нет. 1 месяц или одна неделя это прям как какое-то магическое число в коде. Ну и команда это не обязательно именно 5-7 человек может быть поменьше или побольше.

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

Отличная статья. С очень важным смыслом: здравые идеи практически всегда извращаются людьми, игнорирующими контекст. У каждого решения или концепции есть "рамка" - набор ограничений, задающих контекст применимости.

Математикам эта история очень понятна. Это называется "область определения". За границами которых формула (теорема, метод) не просто работают хуже - они не работают совсем.

Как, например, коммунизм - великолепная идея, но с контекстом "нужен новый человек - с этими не взлетит". И не взлетело, что характерно.

Или как любой аджайл, который создавался разными авторами как способ эффективной работы команд профессионалов (включая представителей заказчика), а в массовом внедрении куда и как попало - превратился в самооправдание предельной неэффективности толпы ламеров (включая, опять-таки, представителей заказчика)

Из инженерных примеров можно еще вспомнить злополучный MVC, родом из того же PARC. Отличную концепцию основного рабочего цикла для простого текстового редактора - стали совать везде где попало. Результат за окном - целый куст вариаций на тему, порожденный попытками вкорячить простую и ясную архитектуру далеко за пределами ее границ применимости.

>например, коммунизм - великолепная идея, но с контекстом

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

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

С другой стороны есть сферы вроде например сферы услуг и культуры где социалистические принципы не работают совсем. Даже в СССР тот же кинематограф были вынуждены хотя бы частично организовывать по рыночным принципам, потому что иначе вообще не работало, и все равно результат намного хуже был. Когда пытаются организовывать социалистическими принципами работу ресторанов, производителей одежды и парикмахерских, писателей и художников, получается полная дрянь.

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

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

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

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

К сожалению про такой подход мало говорят и пишут, как правило мне попадаются упертые фанатики либо рынка вообще везде либо коммунизма-социализма вообще везде. Хотя у меня такое ощущение, что в развитых странах его на практике и применяют.

Не ожидал увидеть в треде о Scrum и ООП [очередное] обсуждение коммунизма, но [в очередной раз] не могу пройти мимо.

Построенная чисто на рыночных принципах медицина в США

Медицина в США перестала быть чисто рыночной где-то между 1950-ми и 1970-ми (например, те же CON — очень рыночно), и исход немного предсказуем: количество проедающих деньги бюрократов попёрло вверх:

а вместе с проедающими деньги бюрократами попёрли вверх и косты:

При этом государственные траты на медицину растут почти так же, как частные:

При этом куда менее регулируемые частные косметические процедуры растут даже меньше инфляции:

хотя количество одних только косметических операций выросло (с 97-го, раньше статистику найти сложнее) в два раза, а всех вмешательств — в шесть раз.

по вполне объективным показателя построенной на социалистических принципах медицине тех же стран Европы

Не существует объективных показателей, способных сравнить существенно рыночные и существенно нерыночные системы. Вы можете спрашивать, например, «сколько трудового времени нужно потратить человеку из X-перцентиля по доходам для получения такого-то ухода, и как быстро он его получит», и Европа стремится, чтобы для всех X это было примерно одинаковым. Лично мне совершенно неважно, чтобы это было одинаковым, и лично мне важнее, чтобы лично мне услуги были доступны за малое количество моих усилий и быстро.

В США они мне так доступны. В Европе — нет. В Европе я за полтора года не дождался очереди на очки. В Европе одна моя родственница умерла от рака, потому что ждала в очереди на обследование полгода и дождалась до неоперабельной стадии (но да, после постановки диагноза ей бесплатно соцработница носила продукты или чё-т такое). В Европе одна моя знакомая по работе стояла в очереди в больницу по ментальным вопросам, простояла два года, больница закрылась, пришлось перейти в другую больницу и стоять в очередь туда заново. В Европе NHS оценит вашу жизнь и решит, нужно ли вам делать операцию или нет. Или вы суициднитесь, пока будете ждать в очереди. Или будете выдирать сами себе зубы. Или г-во решит, что вам лучше умереть, чем собрать деньги себе на лечение заграницей (и взять государственные), и запретит про это рассказывать. Или вы с супругой выберете эвтаназию вместо того, чтобы ждать очереди в NHS. Или NHS решит, что ребёнку не надо ехать в Италию за лечением, хотя там всё готово, и ребёнок просто сдохнет.

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

Но да, зато всем всё бесплатно (почти).

Не, спасибо, я лучше с осознаваемой ответственностью за свою жизнь, и в долгах, но живым. И, собсна, про NHS у меня куча ссылок, выше — так, мелкая выборка, и каждый раз, когда я их читаю, я вспоминаю, что коммунисты — не люди, а желающие власти без всякой ответственности упыри, и вспоминаю некоего

Rafał Gan-Ganowicz

When asked how it felt to take human life, I wouldn't know, I've only killed communists.

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

Про деньги я уже написал выше, а про продолжительность жизни — на те же фентаниловые смерти [собственный выбор; сильно положительно коррелирует с социальностью штата] и ожирение [почти всегда собственный выбор] сделали коррекцию? Я коррекцию делал, и учёт одних только фентаниловых смертей резко бампал продолжительность жизни в Штатах на несколько лет.

Есть очень хороший показатель эффективности хелскера: отношение cancer incidence rate и cancer death rate. Вы хотите, чтобы это отношение было выше: тогда больше рака выявляется, и меньше людей от него умирает. И, сюрприз, США и тут впереди (если сравнить с Канадой, Францией и Германией, например) в полтора-два раза:

  • US: 367 / 181 = 2.03

  • Germany: 274 / 301 = 0.9

  • France: 339 / 288 = 1.17

  • Canada: 345 / 257 = 1.34

Организовывать работу полиции и армии по рыночным принципам по моему не пытается уже никто вообще в мире.

Лучший подход к полиции — это вооружённое население, конечно. Децентрализовано, рыночно, эффективно.

Да и к армии тоже. Попробуйте это в мичети.

Ну и это уже не говоря о том, что рынок без государственного присмотра практически неизбежно скатывается в монополизацию

Нет, конечно, но мой комментарий уже и так слишком длинный.

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

Совершенно негодный критерий, соответственно остальное комментировать - теряет смысл. Собственно, вот это и есть упыризм, коий еще больше демонстрируется цитатой:

я вспоминаю, что коммунисты — не люди, а желающие власти без всякой ответственности упыри, и вспоминаю некоего Rafał Gan-Ganowicz When asked how it felt to take human life, I wouldn't know, I've only killed communists.

Да и какие в Европе коммунисты, лул.

Я коррекцию делал, и учёт одних только фентаниловых смертей резко бампал продолжительность жизни в Штатах на несколько лет.

Какое великолепное натягивание совы на глобус, подгонка под результат!

Лучший подход к полиции — это вооружённое население, конечно. Децентрализовано, рыночно, эффективно. Да и к армии тоже

Крайне отвратительное устройство общества - регрессия в архаику, прогресс невозможен. Достаточно посмотреть количество нобелевских лауреатов на Диком Западе.

Нет, конечно, но мой комментарий уже и так слишком длинный.

Разумеется скатывается, примеров тому в истории валом. Это неизбежное свойство.

Почему же, взлетел. Только не коммунизм, а социализм. Свалился, когда снова повылезали слишком старые люди. Тем не менее, формации эволюционируют (феодализм таки кончился, несмотря на провалы революций 17-18 веков), аналогично и в инженерии.

Что пошло не так

Всю проблему с ООП можно описать одним предложением: "Её используют там, где не нужно, и не используют там, где нужно"

ну и, где примеры? Судя по всему, очередная статья написанная с помощью нейронки

Мне чистый Скрам (даже и с месячными спринтами) для крупных проектов кажется очень сомнительным приемом - все же для полноценного проектирования сложной системы надо ее более-менее видеть целиком (без деталей, но общие границы и контексты), а что увидит аналитик за неделю? Да он банально не успеет опросить всех нужных специалистов заказчика. Тут логично делать первые спринты только на стратегическую аналитику и проектирование архитектуры (чтобы аналитик с заказчиком не напридумывали нереализуемого), потом спринт более детальной архитектуры и только потом реализация блоков системы при помощи классической модели скрама. А между несколькими релизами вставлять еще спринт аналитики и архитектуры.

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

Боюсь, беда скрама в другом:

В корпоративном мире балом правят бюджет, сроки, ресурсы. И тут вдруг модным стал скрам.

Результат: ты по-прежнему не имеешь права выйти за рамки треугольника сроки-бюджет-ресурсы, но обязан стать гибким и выдавать результат сразу.

Значит, нет времени на полноценную аналитику. Все прогнозы получаются даже хуже, чем при водопаде. А еще в любом спринте после демо бизнес меняет требования или расширяет скоуп. Имеет право, мы ведь по скраму работаем. Бюджет и сроки, при этом - табу.

Тема хорошая и правильная. Но что интересно: и сама статья, и — особенно! — комментарии к ней являются изумительной иллюстрацией к этой теме.

(Не буду говорить про Scrum ` не очень интересно, но, чувствую, с ним та же история.) А с нормальным ООП ни автор, ни комментаторы так и не захотели ведь разобраться! Гораздо проще ведь в очередной раз перемалывать банальную истину про то, что "у каждой технологии есть своя область", не уточняя, где же она находится.

А автор ещё и пропаганду ФП умудрился ввернуть Не совсем беспочвенно, но и не очень-то способствует лучшем пониманию.

Автор демонстрирует какое-то фундаментальное непонимание того, что такое OOP. Похоже, он перепутал все с EDA или я даже не знаю с чем еще.

Много лет назад, когда вопросы по ООП на собеседованиях были частью "обязательной программы", мне казалось, что это простая базовая, для любого программиста, концепция, которую все вокруг, +/-, понимают одинаково и которая не вызывает ни в ком никаких особых затруднений. Но, в последнее время, я стал встречать все больше и больше "разоблачающих" статей, заставляющих меня чувствовать себя динозавром среди мамкиных реформаторов и потрясателей основ. Вопрос: ребята, вы откуда это все берете? Что вы там курите? Как насчет почитать что-нибудь из классики или, на худой конец, в википедию заглянуть? Чат-гпт еще можно спросить, если времени так мало...

Это как раз именно от того, что в массы шло примитизированное понимание, никакой эрудиции.

I made up the term 'object-oriented', and I can tell you I didn't have C++ in mind.
-- Alan Kay, OOPSLA '97.

https://softwareengineering.stackexchange.com/questions/46592/so-what-did-alan-kay-really-mean-by-the-term-object-oriented

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

ИМХО 1) Почему ООП получил такое распространение? КМК потому, что это единственная парадигма программирования, в которй хотя бы как-то связно разработана пошаговая методика декомпозиции, которой можно обучать. Можно с ней спорить, можно ее не любить, но она хотя бы есть. Другие "Волшебные слова", такие как KISS, SOLID, больше напоминают набор благих пожеланий.

ИМХО 2) Что мне не нравится 1) То, что ООП пихают во все области, даже там, где оно не очень-то и подходит.
ИМХО 3) Что мне не нравится 2) Засилие в ООП Явистов И C++-ников, они тащат свое видение ООП везде, в том числе и в Питон. Это приводит к сумашедшему оверинжинерингу.

ИМХО 4) Моя практика 1) Иногда полезно придумать свою парадигму и писать в ней, если придуманная парадигма хорошо ложится на задачу. Например писать на классах, но не в ООП.

Где-то так. Простите за сумбур.

Автор ведет историю ООП от Smalltalk-71, где действительно во главу угла поставлен обмен сообщениями. Но. До этого была Simula-67, в которой сообщения были единственного типа, ориентированного только на моделирование параллельных процессов. А объектный подход был в полный рост.

В книге по структурному программированию (кажется у Митчел) приводится забавная классификация программ.

1.Программа-зубочистка: сделал, использовал, выбросил.

2.Программа-молоток: сделал и пользуешься пока удобно. Перестанет - делаешь другую.

3.Программа-небоскреб: будет работать долго и рано или поздно кому-то придется вносить изменения.

Появление третьей разновидности привело к появлению структурного программирования, когда понятность программы важнее эффективности.

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

А программирование интерфейсов - маленькая полянка на которой тоже удобно использовать ООП. Но и только.

Мы сейчас ведем проект, который частично написан в функциональной среде, а частично в ООП. И хорошо видно, какие подзадачи удобно писать так, а какие эдак.

ООП - это парадигма, которая рассматривает систему как набор объектов, обменивающихся сообщениями

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

Нет смысла моделировать всю систему через объекты. Если предположить, что "Программная система - это набор UseCase'ов по передаче, трансформации и хранению данных", то ООП хорошо справляется только с "передачей". Для трансформации лучше всего подходит функциональная парадигма. Для хранения (чтение и запись) - реляционная.

Таки есть - разбить систему на слабо связанные между собой модули со своими собствеными данными. Если это удается, то сложность системы понизится. А уж обработку данных для модуля можно вести и в функциональной парадигме, если так хочется, и это будет менее сложно: меньше данных и зависимостей между ними.

PS Реляционная - это вообще не парадигма программирования, это - модель хранения данных. А язык SQL - это реализация декларативной парадигмы программирования, которая, вообще-то может применяться для разных схем хранения данных.

Реляционная модель это математика, которая включает в себя структуры и алгебру. Хранением занимаются СУБД, но можно и без них. Разница такая же как между строками и файлами,.графами и графовыми базами данных, и т.п.

Нет, изначальное ООП - именно про обмен сообщениями. Большинство нынешних не являются ООП в том смысле.

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

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

PS Реляционная - это вообще не парадигма программирования, это - модель хранения данных

А здесь тонкий намёк, что (современное мейнстримное) ООП является необоснованной чепухой, в отличие от реляционной алегбры, за которой стоит математика.

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

Статья более бессмысленная?

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

Аналитики не могут собирать информацию для следующих спринтов?

В чистой теории вроде нет.

  1. Нет аналитиков - есть уникоманда

  2. Любой результат работы должен быть готов к демонстрации заказчику и [желательно] отгрузке в прод.

Помните, что ООП - это про посылку сообщений.

Как там у вас в 70-х? Доллар по 15 копеек? Хотя какой доллар, о чем я... Просто уже довольно давно все полезное, что было в обмене сообщениями, называют модель акторов. А ООП - это теперь немного про другое: наследование, полиморфизм, вот это вот все.

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

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

И прошу, не надо больше кошек, собак и машин

Сколько будет 1+1? Правильно, 100100. Потому что первое число записано в метрах, второе в километрах, а результат в сантиметрах. Вот для чего нужны кошечки и собачки - чтобы при их сложении получить коллекцию домашних животных, а умножать и вовсе было нельзя.

Причём тут кошечки и собачки, когда это просто система типов, а ООП до потери смысла нетипизирован [по замыслу создателей smalltalk]?

При том, что наследование типов, равно как и их явное/неявное преобразование - это результат эволюции идей ООП и никаких исходных смыслов вовсе не потерявшее, а непосредственно сам smalltalk давно канул в лету.

А где и зачем здесь наследование типов?

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

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

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

Интересная статья, спасибо автору. Но ещё интересней комментарии под статьей.

Эта история началась в Xerox PARC. Инженеры этого исследовательского центра работали над революционной идеей: оконным графическим интерфейсом.

Чтобы понимать контекст: в то время все операции производились через консоль или перфокарты. Компьютерных мышей тогда не существовало даже как концепции. Так что идею создать первый в мире интерфейс с окнами и кнопками, который управляется компьютерной мышью, можно по праву назвать революционной.

Штааа?

Инженеры в Xerox PARC "начали" работать над оконным интерфейсом тогда, когда в 1970 году половина команды Дугласа Энгельбарта свалила из его ARC в Xerox.

В ARC же Энгельбарт (и его команда) как раз и занимался оконными интерфейсами и мышью. Собственно он в 1968 году и продемонстрировал миру первый рабочий прототип мыши на The Mother of All Demos.
При этом концепция окон у Энгельбарта не исключала, а вполне себе базировалась на консольном вводе - несколько окон и в каждом по консоли.

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

Когда вижу такие обороты, всегда недоумеваю:
-автор так тонко проверяет читателей на знание отраслевой истории и пытается "поджечь" историческую ветку обсуждения?
-или он почерпнул инфу в каком-то пересказе пересказа, а при подготовки статьи ему было недосуг провести фактчек своих воспоминаний?

Для моделирования предметных областей лучше всего подойдёт ФП

Когда лет 15 назад я, будучи весьма молодым инженером пил чай с 45-летним PM-ом, (окончившим ВМК и проработавшим в отрасли четверть века) и интересовался у него, куда бы лучше развиваться, я спросил у него про ФП.
Он слегка улыбнулся и ответил:
-Когда я учился в ВУЗе, ФП было очень перспективным направлением, за ним было будущее. Насколько я вижу, по прошествии пары десятков лет ФП всё ещё перспективен и будущее по прежнему за ним. Думаю, так оно будет и ещё через 20 лет :))

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

Единственная книга по ФП, которая нормально объяснила мне, что такое ФП и в чём его преимущества - эта так, которую я упомянул в статье.

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

Работал в разных скрам проектах начиная с самого хайпа. Видел много бреда, когда работники не понимали что и зачем они делают, но им приказали "скрам", они стараются.

Результат: карго-скрам катастрофа, где каждый день доходит до абсурда. Все в недоумении и тратят время на ненужные дискусси и оформление. Зато в отчётах "у нас скрам".

Скрам идея всё таки требует немного интеллекта, из-за своей гибкости/свободы в реализации, а не у всех есть такой ресурс 🙂

На счёт интеллекта вы правы, и его требует любая деятельность XD. Но моё мнение: главная проблема с Agile не в гибкости / свободе реализации.

Agile СЛОЖНЫЙ. Его манифест состоит из 4 компромиссов и 12 принципов. Это выходит за любые пределы когнитивной нагрузки. Не говоря уже о том, что строить всю модель на компромиссах - это самоубийство. Что мы и видим на практике: Agile закономерно превратился в "Fragile"

Agile нужно упрощать, если мы хотим изменить ситуацию

Коллега написал про ООП, забыв про ООА и ООD. Также коллега ничего не сказал о DDD. OO подход в разработке (работе с требованиями, проектировании, программировании) - это активная история 90-х и 2000-х. Я в этом время стартовал свою проф. деятельность. В основе лежит восприятие человеком внешнего мира. ОО базировалось на теории типов, теории категорий, теории множеств и в целом дополняло имеющуюся на тот момент реляционную теорию с теми же корнями. А использование Xerox объектов для организации кода - лишь проявление общего подхода применительно к UI слою и немного сомнительно что Xerox был первым в ООП, учитывая наличие реляционной теории до из 70-х. Последняя с основой в теории множеств, повторюсь, как и ОО.

Ключевое в ОО подходе - это восприятие мира человеком и коммуникация человеком информацией о мире. Было декларировано, что восприятие объектно (мы воспринимаем объекты), и коммуницируем мы информацией об объектах (говоря о собаке соседа) и абстрактно об их классах (говоря о собаках в целом). А так как машина предназначена для человека, то отражения (подчеркну) отражения объектов реального мира в структуру программ (мы называем это моделью предметной области) - хороший способ их [программ] организации (о чем собственно и пишут в комментариях).

А далее выяснилось, что много предметных областей, которые существуют только в рамках компьютерных вычислений. Например - окошки, формочки и т.п. Но они то же perceivable (воспринимаемы как объекты) и хорошие кандидаты на классы.

NB: Статья чуток поверхностная и тема ОО не раскрыта даже на 10%. Scrum нет нужды оценивать, так опять же нужно было углубиться в инженерный процесс как основу и его приложения в различных контекстах (это не простая тема). Scrum - это упрощение реального процесса, организационный framework, усреднение индивидов до team collaboration (недостатков), с заявленной потенциальной пользой от возникающей синергии и накатки одного и того же процесса (выгоды). Но тут пусть другие. Не люблю Scrum.

Удивительно, что такой спорный текст настолько крепко заплюсован, хабр, что с тобой? Почему спорный?

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


Во-вторых про scrum/agile, автор как будто достал из запасников старую телегу про то, что ваш скрам "недостаточно чистый", отсюда и все ваши проблемы (наймите годных скрам мастеров и у вас всё станет хорошо). Казалось бы, это обсуждалось уже миллионы раз в том числе на хабре, что скрам как методология ничего не гарантирует. Даже если вы всё делаете идеально по методичке с месячными спринтами под руководством скрам гуру за 500$/час, но при этом с плохой командой, это никогда не будет работать лучше, чем с "адаптированным" скрамом, с нарушенными и мутировавшими в сторону "чуть больше вотерфола" "ритуалами", которые хорошая команда продумала под свою конкретную ситуацию.

В-третьих про ООП. "Её используют там, где не нужно, и не используют там, где нужно". Раньше за такие утверждения на хабре карали минусами в карму и не спроста, ведь это утверждение как минимум ложно, поскольку наверно кто-то где-то всё таки её использует по назначению, хотя бы чисто статистически не так ли? Или автор не допускает такой возможности? А дальше автора понесло.

"Нет смысла моделировать всю систему через объекты. Если предположить, что "Программная система - это набор UseCase'ов по передаче, трансформации и хранению данных", то ООП хорошо справляется только с "передачей". Для трансформации лучше всего подходит функциональная парадигма. Для хранения (чтение и запись) - реляционная."

А если я скажу, что есть ситуации, где с помощью ООП реализована функциональная парадигма трансформации данных? Например в Java Stream Api, функциональный пайплайн преобразования данных описан в виде набора полновесных объектов, "обменивающихся сообщениями", при этом у вас есть плюсы как функционального подхода, так и объектно ориентированного, как в этом случае быть? А как быть с тем, что люди работающие в ООП языках уже давно не пишут SQL руками, а работают с "чтением/записью" с помощью ОО фреймворков, которые в свою очередь переводят их запросы в "реляционный" SQL автоматически, а часто и в языки работы с данными других парадигм хранения? Да, они кому-то не нравятся, такие люди есть, но даже эти люди крайне редко отказываются от них в пользу голого SQL просто потому что это удобнее, проще и ближе по уровню абстракции к тому, что они делают. И про другие парадигмы хранения, автор никогда не слышал про KV-store хранилища данных? Если мы говорим про самую простую и понятную модель "чтения/записи", то мы должны говорить именно про них, а не про мягко скажем не самую простую "реляционную" парадигму, которую хорошо понимают далеко не все из тех, кто ей пользуется.

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

1) Хотел назвать "Идеи потерявшие суть", но не звучало. В ретроспективе понимаю что да, следовало бы назвать по другому

2) Не приписывайте мне слов, которых я не писал. Я не имею мнения относительно Scrum-мастеров, гарантий эффективности и "деятельности по методичке". Я лишь показываю причина-следственную связь: если вы выкидываете или изменяете в методологии места, которые являются её основой (оставляя всё остальное без изменений) - то она 100% не будет работать

3) В моём утверждении не было модальности (все; большинство; меньше половины и т.п."

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

Касательно SCRUM, Автор либо никогда не руководил командами разработки, либо никогда не работал в компаниях с развитой Agile культурой.

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

Напомню наши ценности из начала статьи: Адаптация, Ценность, Команда. Если вы выкидываете хотя бы один из ритуалов, то забудьте об Адаптации. Никаких улучшений со временем не будет.

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

Я работаю в R&D и пожалуй могу сказать, что моя специализация -- научные вычисления. Результат моей работы не часто можно увидеть и пощупать, особенно если хочется увидеть разницу разницей продукта сейчас и месяц назад. Оригинальную книгу по скраму не читал (не горжусь этим, просто привожу для контекста), но считаю, что работаю по скраму, мой выбор основан на прошлом опыте и своем личном и коллег по цеху, что лучше со спринтами, чем без. Мои спринты длятся 2 недели, именно такая длительность основана на обратной связи, а не потому что так советуют в книгах.

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

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

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

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации