Обновить

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

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

Всегда хочется спросить - "когда ты написал свою миллионную LOC?"

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

Дополню в ту же тему

Проблема Х

Все преимущества Раста показываются на примерах уровня хелловорлда - пара строк кода. И эта статья не исключение.

А если мы начинаем смотреть реальные проекты на Раст - "там водятся тигры" (С)

Я не говорю, что Раст совсем плохой - наоборот, в нем многое сделано хорошо (да, но далеко не все). Но показать это надо правильно - на реальных задачах.

Я про след ФП в других языках расскажу в следующей главе ;)

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

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

Citation needed.

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

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

Проблема II: Императивность

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

Проблема III: Некорректная или недостаточная обработка ошибок.

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

Проблема исключений № 1: Вы не знаете, что именно может упасть.

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

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

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

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

Это не совсем так, #[no_panic] может в рамках крейта доказывать, что указанная функции не паникует. А при сборке с LTO, и с учётом других крейтов (транзитивно).

Ну так это сторонняя библиотека и вроде как у неё есть какие-то заковырки? В любом случае не часть стандартной поставки языка

Да, это сторонняя библиотека, но там нет никакой магии и заковырок, всего 209 строчек кода. Работает она как раз за счёт того, что в Rust достаточно легко найти все места где может паниковать программа panic="unwind".

Ну и ничего из перечисленного не является какой-то специфической особенностью именно вот функционального стиля.

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

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

Похая нейростатья, с дурацкими примерами.

Функциональный стиль не является декларативным ни в коем разе, и все эти фразочки "вы описываете, ЧТО вы хотите, а не КАК это сделать" не объясняют ничего. Функциональный стиль является функциональным, и демонстрировать его отличие от не-функционального надо так:

Сейчас будет не-функциональный (процедурный)

int a;
int b;
int result;
void calculate() {
  sum = (2*a + 3*b)/7 - 5;
}
void main() {
  a = 5;
  b = 8;
  calculate();
  print("Result is:" + result);
}

А сейчас - функциональный:

int calculate(int a, int b) {
 return (2*a + 3*b)/7 - 5;
}

void main() {
  int a = 5;
  int b = 8;
  int result = calculate(a, b);
  print("Result is:" + result);
}

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

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

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

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

Спасибо!

Я хотел чуть раскрыть тут мысль - явные thunk'и, кмк, являются слишком низкоуровневым средством. Это как ассемблер по сравнению со структурным программированием. То есть, не очень хватает тут экспрессивности текущих языков.

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

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

«Правильно поставленный вопрос содержит половину ответа». Это же часть вопроса — что же именно мы хотим выражать... Я чувствую, что вот этих seq и строгих аргументов не хватает — слишком низкоуровнево. Строгие интерфейсы модулей? Чересчур банально.

И пару слов про microHS черкануть в формате поста можешь?

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

1.

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

А я разве так в статье не сказал? ;)

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

2.

Вы умудрились попасть в едва ли не единственную фичу тайпскрипта, которая доживает до рантайма: Enums are real objects that exist at runtime (source)

Про вот это не знал, спасибо!

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

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

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

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

Вопрос - нахрена, а главное - зачем ?! Если объективно процесс ествественным образом описывается как набор шагов, меняющих состояние системы - то почему бы не последовать совету берестяной грамоты N35, и не описать его на языке программирования тем же (т.е. императивным) способом ? В чем смысл натягивания совы на глобус и вытягивания цепочки лямбд - которые потом еще хрен отладишь нормально (потому что не видны промежуточные преобразования коллекций) ?

Я лично вижу два случая, когда ФП надо применять:

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

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

Ну и до кучи скажу что архитектура современных ЭВМ нихрена не заточена под ФП - она заточена под императивное программирование с мутабельным состоянием в RAM. Соответственно, ФП всегда будет подвергаться performance penalty по сравнению с императивным кодом. Другое дело - что производительность софта за последние десятилетия опустили на такое дно, что добавление туда еще и ФП - уже существенно ничего не меняет...

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

Вот вы удивитесь - но если я кому-то буду рассказывать как я хожу в магазин - то это будет именно пошаговое перечисление действий: одеваюсь и оказываюсь одет, выхожу и оказываюсь на улице, двигаюсь в разных направлениях по улицам и перекресткам - оказываюсь у дверей магазина, набираю продукты, расплачиваюсь на кассе, и т.д. А вот сайд-эффектами этой деятельности оказываются a) уменьшившееся количество денег на счете и b) увеличившееся количество продуктов в холодильнике. И да, можно дискутировать о том до какой степени детализовать этот процесс (в программировании мы обычно детализируем до stdlib call или syscall). Без шуток - покажите мне человека который будет описывать поход в магазин как функциональное преобразование цифр в продукты... "А папа-мама не сумасшедши ли ?" (C) Мультик

Дальше - аргументы о том, что проще рассуждать и доказывать. Охотно верю - но знаете, идите на матфак работать с такими аргументами, что-ли ? Там любят таких... А я тут у мамы инженер. Мне не надо чтобы красиво доказано - мне надо чтобы работало, и легко читалось. Понятно что когда у меня есть streams, то ими надо пользоваться - например не писать свой алгоритм сортировки, а передать лямбду в библиотечный sort(). А когда один черт в коде клеится весь алгоритм, но пересыпается .apply() .with() .orElse() и прочей матерщиной (привет Kotlin!) - очень хочется дать по башке, и отправить копию новгородской грамоты чтобы лучше доходило...

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

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

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

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

Я видел развитие языков программирования через руки - начиная от 8-бит ассемблера, и дальше через 80x86 к C, C++, Java и так далее. Никакого движения в сторону FP там не было. Есть объекты реального мира. У них объективно есть состояние, которое меняется со временем по присущим им законам - или под внешним воздействием. В программе мы строим модель этого объекта или явления, отображая законы реального мира в их подобия в модельном пространстве. То-есть заменяем реальный вес на последовательность битов, его кодирующую (с фиксированной или плавающей точкой), положение объекта в пространстве на значения эйлеровых углов (или чего-то посложнее), и так далее. На низком уровне (ассемблере) - не существует способов выразить в языке структуру модели - какая ячейка или регистр за что отвечает - приходится держать в голове. Все дальнейшие нормальные языки: от прости господи, бейсика - до джавы - вводили разные изобратительные средства чтобы отразить объективно существующую структуру реального мира в программе с минимальными искажениями. То есть - группировать признаки относящиеся к одному модельному объекту в структуры, и сделать сложнее некорректные действия - типа вызвать метод "покрутить хвостом" к кухонной двери вместо кошки. Никто, щука, никогда не пытался представить модель мира как комбинацию бесчисленного количества чистых функций.

При том, что интеллектуально я готов принять множественность описания объектов реального мира (например, описание сигналов в частотной области спектрами, или во временной - графиками) - идея описать всё сущее чистыми функциями - проходит в моей канцелярии как забавный курьез. Ну и что, что можно ?! Вон метафора конечных автоматов тоже полна по Тьюирнгу - и любой процесс или явление можно описать таким образом. И есть чудаки которые до сих пор носятся с концепцией автоматного программирования. И аргументы у них точно такие же как у вас: это простой формализм, компилятору легче оптимизировать, и т.д. Проблема в том, что существует все-таки естественное описание объекта, которое является предпочтительным. Есть природные явления которые прямо-таки созданы для описания КА (или ФП). И там их надо применять. А там где это не естественно - там начинаются натягивания совы на глобус: давайте создадим скрытые фиктивные состояния, давайте добавим фиктивные входные и выходные сигналы, и т.д.

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

Любая абстракция лишь примитивное упрощение реальности мира.

В этом смысле они равноудалены и не особо лучше одна другой)

Я предпочитаю: "Все модели неверны, но некоторые полезны" (C) Дж.Бокс.

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

Олсо, это ведь работает во все стороны: сколько людей описывает поход в магазин как ООП? Сколько людей… инстанциирует класс бумажника? обращается к синглтону бумажников? использует абстрактный класс ИбуМажник? Чей метод вызывается при обмене денег на товар — бумажника, денег, товара, продавца, или абстрактного менеджера сделок? Вы когда ребёнку будете объяснять процесс похода в магазин, будете рассказывать про абстрактный менеджер сделок?

Ха-ха-ха Execution in the Kingdom of Nouns - 20 лет прошло, а ничего не поменялось!
Символическое в ООП — более воображаемое и нарративное. Оно создаёт красивую историю: «объекты взаимодействуют, как люди/вещи в мире».
ФП требует абстрактного, математического мышления: данные отдельно, поведение (функции) отдельно, всё immutable. Это мощно, но когнитивно дороже для большинства.
Моя голова устает сильнее когда я программирую на ФП.

Однако - если мы посмотрим на окружающий мир, то очень сложно найти действие без среды или актора, который его инициирует. Замечу, что безличные предложения типа: "на улице дует" или "надо взять мусор и вынести" - это артефакты человеческого языка, который позволяет такую компрессию - опускать подразумеваемые вещи. В реальности на улице дует ветер (сиречь направленный поток молекул воздуха), а для того чтобы взять мусор и его вынести - нужен объект с соответствующими type traits (например, домашние животные не способны к этому действию).

Поэтому, включение действий в состав объекта-актора я нахожу в большинстве случаев оправданной. Разумеется, могут существовать действия, акторов для которых вы по той или иной причине не включили в состав модели. В языках типа C/C++ или Kotlin никто вам не мешает определить глобальную функцию для этого. В чистой джаве, да - придется создать God-object с именем GlobalModel, или ModelRules или что-то такое. Что в общем-то тоже имеет смысл, поскольку ваши действия возникающие "по щучьему велению" - являются артефактом конкретной выбранной модели - и в другой модели вполне могут появляться естественным взаимодействием акторов. Также, собирание "произвольных" действий в God-object может быть весьма полезно, если у вас в программе есть несколько моделей явлений одновременно. Если определять глобальные функции - то рано или поздно случится name-clash, и придется их разводить через дополнительный объект namespace. А поскольку вы даже и в джаве можете импортировать статическую функцию из god-object (и вызывать ее без явного указания объекта) - такой объект IMHO ничем не отличается от namespace в C++...

Вы не услышали собеседников: меня и @IUIUIUIUIUIUIUI

Если данные хранятся персистентно в БД и мы использует ActiveRecord, то БД становится онтологией "мира". Но нам нужно познание, через эпистемологические элементы. Поэтому придумано ООП, которое связано с БД через ORM. Адам Рид в 2003 году) проводили параллели между ООП и объективистской эпистемологией Айн Рэнд.
Object-Oriented Programming and Objectivist Epistemology: Parallels and Implications
Ключевые параллели, которые она проводит:
Класс (Class) — Понятие (Concept)
Объект (Instance) — Единичный объект реальности (перцепт)
Инкапсуляция — Единица знания как целостная сущность
Наследование (Inheritance) — Иерархия понятий от общего к частному
Полиморфизм — Контекстуальность понятий
Сообщения между объектами — Логические отношения между понятиями.

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

В ФП + теория категорий эпистемологии гораздо ближе к классической математической — (знание как доказательство).
Знание ценится не по тому, насколько «правильно» описывает сущность (как в ООП), а по тому, насколько хорошо оно компонуется с другим знанием. Лучшее знание — то, которое можно свободно вставлять в любое место без побочных эффектов.
Вместо «что такое объект?», мы спрашиваем: «какое универсальное свойство он несет?» (сложение, произведение...)
Явное отделение чистого знания от взаимодействия с Реальным через монады.
Естественные преобразования (natural transformations) Один из самых красивых концептов. Они описывают, как два разных способа преобразования данных соотносятся естественным образом, независимо от конкретного типа.

Не удивительно почему @IUIUIUIUIUIUIUI не выбирал объективизм ООП, он же либертарианец, которых Айн Рэнд резко осуждала.

ООП невыносим для либертарианеца - Functional programming is the libertarianism of software engineering, а нам ближе классовый подход (это шутка еслив что)

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

Есть такой чел Felipe Zapata я за ним слежу с 2013 г как он написал статью Haskell ab initio: the Hartree-Fock Method in Haskell — а репа его 10 лет уже в архиве, потому что Haskell годен только для красивых журнальных статей и всяких бла-бла-блашек типа Learn Quantum Mechanics with Haskell.

Я тоже самое могу на Python + Numba написать с помощью готовых boxed-библиотек типа Numpy и будет работать не сильно медленнее Fortran (правда памяти жрет как не в себя, но это скипнем).

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

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

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

акторы ортогональны фп и ооп

Серьёзно? Может, и electron.quantumPhaseTransition() — интуитивно понятнее, чем quantumPhaseTransition(electron)?

электронный_газ = QuantumSystem([electron1, electron2, ....electron99])
электронный_газ.перейти_в_основное_состояние()
электронный_газ.PhaseTransition()

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

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

Содержимое учебников по квантам спокойно перекладывается на функциональный стиль по построению: есть данные (состояние частиц), на них действуют какие-то функции, получаются новые состояния.

Это "функции" называются операторами и они линейные  — из этого следуют всякие финтифлюшки, типа no-cloning theorem.
Задача в том чтобы найти собственное значение и собственную функцию оператора. Даже в HF-приближении (без учета электронных корреляций вообще), состояние включает расчет двухэлектронных интегралов
(μν|λσ) = ∫∫ φ_μ(r₁)φ_ν(r₁) · 1/r₁₂ · φ_λ(r₂)φ_σ(r₂) dr₁dr₂
— это четыре базисных индекса, то есть O(N⁴) объектов.
При N_электронов=1000 это 10¹² интегралов, а если использовать CCSD(T), то уже O(N⁵), а точная волновая функция экспоненциально сложна. Это как смоделировать квантовый компьютер на классическом только приближенно, пользуясь тем что постоянная тонкой структуры равна 1/137, а в химической связи участвуют только внешние электроны.

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

Можно конечно для молекулы водорода (N=2) написать на Haskell и гордится этим, но нужно для любого N, а тут только суровое ООП (даже нашел непопсовый опенсосрс).

Есть еще более непопсовый DSL-подход Tensor Contraction Engine отвечает на другой вопрос: зачем вообще писать код руками, если задача уже формально описана. Химик записывает амплитудные уравнения в символическом виде — а TCE компилирует это в оптимизированный Fortran. Вопрос о стиле программирования просто не возникает.

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

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

Постоянно! При свёртке нескольких тензоров порядок операций критически влияет на сложность:
A[i,j] * B[j,k] * C[k,l] → можно свернуть по-разному

  • (AB)C — одна стоимость

  • A(BC) — другая (может быть в тысячи раз дешевле)

Для n тензоров перебор всех порядков — задача NP-сложная.

Эту задачу решает ООП Daniel G. A. Smith opt_einsum, потому что ФП не смог, не смотря на все его типы.

Может, Wavefunction для полиморфизма используется? Ну, класс переопределяет только compute_energy, который не используется в базовом классе, поэтому не кастомайзит поведение, поэтому это, опять же, просто реюз кода и выражение идеи «у меня тут есть функция с возвращаемым параметром Double».

С самого начала написал, что нужно вычислить собственные функции и значения оператора полной энергии (гамильтониана).
Саму собственную функцию сохранять не имеет смысла (она выражена через базисные функции и гига- тера-байты коэффициентов), а собственное значение это Double. Ну может еще нужно 1-е и 2-е производные этого Double по параметрам гамильтониана — координатам атомов, для оптимизации или другие наблюдаемые — например дипольный момент.

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

H̄ = e^{-T} Ĥ e^T = Ĥ + [Ĥ,T] + ½[[Ĥ,T],T] + ⅙[[[Ĥ,T],T],T] + ...

T это тензор и ваше ФП не может этот ряд (обрезанный на n-том члене) быстро посчитать (ну тупо-о-о-е).

У меня ПТСР от этой фигни вообще, потому что лет 10 назад один мой рабочий проект сводился к тому, чтобы переписать такой написанный «учёными»

Мне один tenured-профессора из Кембриджа рассказывал, как ему один программист «переписал» на ФП, после чего программа стала работать в 100 раз медленнее )))

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

В основе есть математическая формула, может надо было ее прочитать? Формулы читать умеете?

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

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

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

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

На квантовых компуктерах ФП-шная парадигма может и сработать, потому что там Wavefunction хранится на q-битах, а собственное значение Double не сложно сохранить в классической памяти, поэтому  Felipe Zapata устал на Haskell писать и что-то мутит для квантовых компуктеров — лекарство от рака изобретает

Содержимое учебников по квантам спокойно перекладывается на функциональный стиль по построению: есть данные (состояние частиц), на них действуют какие-то функции, получаются новые состояния.

Ага-ага! Коллапс волновой функции куда ложится? Какая система типов отражает квантовые состояния?

Попросил Claude написать алгоритм Шора:
/// Алгоритм Шора для факторизации числа N /// Структура: классическая часть (Python/Q# host) + квантовая часть (Q#) /// /// Идея: /// 1. Выбрать случайное a < N /// 2. Квантово найти период r функции f(x) = a^x mod N /// 3. Из периода вычислить множители: gcd(a^(r/2) ± 1, N) namespace Shor { open Microsoft.Quantum.Canon; open Microsoft.Quantum.Intrinsic; open Microsoft.Quantum.Math; open Microsoft.Quantum.Arithmetic; open Microsoft.Quantum.Arrays; open Microsoft.Quantum.Diagnostics;

Опять какая-то императивщина. Ладно, буду 15 раскладывать на множители, с помощью Microsoft.Quantum и вайб-кодинга!

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

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

Это все на JS, который с сильным ФП-уклоном.

Очевидно, не помогло =)

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

Передача замыканий - это относительно новомодная штука, а передачей функций ещё С славился. :-)

Расплодилось великое множество объектно-ориентированных языков: C++, Java, C#, JavaScript

На последнем очень хочется остановиться) можно подумать, что это ошибка, но нет, дальше в списке будет и TypeScript, так что нет, автор явно намеренно записал JavaScript в расплодившиеся объектно-ориентированные языки с начала 90-х.

А теперь следим за руками:

  1. Официальная дата запуска JavaScript - 4 декабря 1995 года.

  2. Классы в JavaScript появились в ECMAScript 2015 (ES6), выпущенном в июне 2015 года.

  3. И даже в таком случае это тоже функции, объявляемые иначе, о чём прямо сказано на MDN - Classes are in fact “special functions”, and just as you can define function expressions and function declarations, a class can be defined in two ways: a class expression or a class declaration

Ничего себе, расплодившийся с начала 90-х ООПшный язык, в котором до 2015 года даже не существовало ключевого слова class) Ну и смысл читать дальше после подобных заявлений? Человек же явно не понимает, о чём говорит

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

Уже давно нет (к вопросу про джаву)

С точки зрения семантики языка - да, это сахар для анонимного класса, реализующего функциональный интерфейс. Детали реализации (MethodHandle, invokedynamic) этого не меняют.

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

Сеньор Матюшкин, вы снова с нами?

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

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

Не понял, если просто добавить поле в структуру, то какое дублирование нужно? В метод конструктор в Self{} дописать поле?)
И какие вы трейты собрались переписывать, когда в трейтах нельзя получить доступ к полю структуры?) Если вы имели ввиде переписывать имплементации, то это ничем не больше чем переписывать теже имплантации в любом ООП языке.

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

Типаж это не только его описание, но так же и его имплементация. Они неотделимы.

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

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

Особенно ярко это проблема на Rust проявляется при динамическом связывании.

ООП при всех его недостатках упрощает и удешевляет развитие программного продукта.

Проблема ромба, исключения в рантайме и хрупкое наследование передают привет) я обо всём этом уже писал в статье.

Хочется дополнить, что если смотреть на языки с точки зрения дилетанта — есть 3 типа:

  1. Быстрое написание, но долгий дебаг

  2. Долгое написание, но отсутствие дебага или лёгкий и быстрый дебаг

  3. C++

Каждый выбирает себе сам ;)

Проблема ромба, исключения в рантайме и хрупкое наследование передают привет

Если для Вас это так, то Вы просто не умеете пользоваться OOП.

Каждый выбирает себе сам

Ну вот дайте пример кода so/dll на Rust, манипулирующий с какой-то структурой, который не пришлось бы переписывать при добавлении поля в эту структуру. Тогда как в случае ООП достаточно было бы просто перегрузить несколько методов.

Сравним и написание, и отладку обоих вариантов. Не против?

Я полагаю, что в серьезных mission-critical системах нужно уходить от fp и stack-return-based моделей языков, в пользу полностью императивных процедурных(no-return) event-driven систем. Tl;dr Можно было бы сгенерить статейку почему wal-executor(fifo) круче стека(lifo+забыли залогать) для какого нибудь финтеха или миссии на марс :)

Состояние автора лучше всего описывается словом "корёжит".

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

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

Но сейчас-то - вы посмотрите сколько радости! А примеры какие живые - старался...

А я разве говорил, что ФП — панацея? Даже в статье написано, где ФП неприменимо ;)

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

Но вопрос зачем приватному полю геттеры и сеттеры

Есть такое радикальное течение ООП (ака секта) , что доступ к любой нутрянке должен быть скрыт извне

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

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

Нет. Можно ставить бп прямо на данные.

А вообще все зависит от ситуации. Надо - делаем, не надо не делаем. А ещё автогенерацию сеттеров можно вспомнить.

Можно ставить бп прямо на данные.

Далеко не все МК это поддерживают, или их катастрофически мало. Например, ESP32 поддерживает всего два watchpoints. Тогда как программных breakpoints можно установить неограниченное количество.

А на практике, совсем не редко тупой сеттер превращается, например, в сеттер с семафором или спинлоком. Поэтому на Rust тоже предпочитаю сеттеры, а не прямой доступ к элементам структуры.

А геттеры всегда зачем?

Тоже самое. Сегодня это буфер своего кода, а завтра он может модифицироваться обычным DMA, чужим кодом с другого ядра, например, ESP-IDF, или автономным блоком, вроде PIO в RP2040.

Нихрена себе. И это все произошло ВНЕЗАПНО?

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

И это все произошло ВНЕЗАПНО?

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

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

Не только. В жизни то 32-х битное целое нужно заменить на 64-битное, то float на decimal, то вообще переменную сделать вычисляемой из нескольких новых, или превратить в объект, поддерживающий NaN и +-Infinity.

Не только. В жизни то 32-х битное целое нужно заменить на 64-битное, то float на decimal, то вообще переменную сделать вычисляемой из нескольких новых, или превратить в объект, поддерживающий NaN и +-Infinity.

И как в этих случаях помогут геттеры? Переписывать 2 метода вместо одного мембера удобнее?

Переписывать 2 метода вместо одного мембера удобнее?

Да, удобнее. Но не вместо одного свойства, а вместо 100500 мест, где к этому свойству обращались.

А что будем делать с типами в 100500 местах обращения?

В случае свойства - жду Ваших предложений.

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

Класс юзается в 20 местах, пропертя задаётся в 40 местах в коде. Будем везде брейкпойнт ставить?

Ещё раз для тугих. Бп на данные. Т.е изменились данные по адресу 0H450000DE

Ещё раз для тугих. Например, в CH32V003 вообще нет watchpoints. А в ESP32 их только два. Или больше двух свойств/переменых Вам никогда не приходилось мониторить?

Я и есп32 ни разу не видел)

Я выше написал - надо - используем.

А секта говорит - надо обязательно всегда.

Почувствуйте разницу

написал - надо - используем.

Откуда это узнать? Вы провидец и точно знаете, какой класс может быть использован в будущем на ESP32 или каком-то ином МК со своими ограничениями, а какой нет?

Нет Вы. Вы же пишете по есп32 а не я. Вот и планируйте.

А вообще есть целое понятие оверинжиниринга

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

С учётом того, что даже на STM32F4, не говоря уже о STM32F7, уже запускали графику под Qt, ситуация далеко не умозрительная.

Конечно я утверждаю обратное.

Если я пишу код под условный gtk+boost+odbc, вряд-ли его сунут в esp32

Более того, в эмбеде С++ не то чтобы особо распространен (из экономии), а с таким подходом и вообще засмеют

Если я пишу код под условный gtk+boost+odbc, вряд-ли его сунут в esp32

Почему? Если на STM32F4 работает Qt, то что мешает там же запустить gtk? Да и на ESP32-P4 это технически вполне возможно, так же как и на RP2350.

Ну и "вряд-ли" и "никогда" - несколько разные понятия )))

в эмбеде С++ не то чтобы особо распространен (из экономии)

Вы отстали от жизни. С появлением в МК поддержки до 1 ГБ PSRAM (в STM32F7, для примера) экономия меняется. Тот же STM32CubeIDE C++ поддерживает из коробки.

Впрочем, даже Arduino IDE для восьмибитных MK вполне себе поддерживает C++, хоть и с рядом ограничений. Скетчей на C++ там немеряно.

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

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

Обоснуйте. Я не вижу почему этот класс не может быть использован, например, на STM32F7 с гигабайтом PSRAM. Да и вообще на любом CPU без аппаратной поддержки виртуальной памяти.

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

Так наоборот, на специализированном МК вполне можно достичь куда большей скорости, чем на CPU общего назначения. В данном случае наличие виртуальной памяти может только увеличить латентность, но уж никак не уменьшить.

А нет менеджера виртуальной памяти - нет и возможности неограниченного использования watchpoints, так как остаётся только весьма ограниченное количество аппаратных watchpoints. ЧТД.

Вы вообще меня удивляете. Неужели впервые слышите о том, что для систем реального времени, где низкая латентность критически важна, специализированные MK и SoC куда более предпочтительны, чем CPU общего назначения?

Именно поэтому в подобных местах повсеместно предпочитают MK и SoC (часто в виде ПЛК), вместо обычных компьютеров.

С FPGA — торгуют, с обычных x86_64 — торгуют

Ну так специализированные MK и SoC и есть золотая середина между FPGA и CPU общего назначения. Причём нисколько не исключают наличие FPGA в своём составе.

для систем реального времени, где низкая латентность критически важна, специализированные MK и SoC куда более предпочтительны, чем CPU общего назначения?

Именно поэтому в подобных местах повсеместно предпочитают MK и SoC (часто в виде ПЛК), вместо обычных компьютеров.

Бред

Чистое дилетантство

Расскажите это технологам на производстве, где даже микросекунды играют роль )))

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

Я разработчик асутп много много лет.

Мне дилетантские заявления непричастных к отрасли людей режут слух

Мне это не нужно. Я знаю технические требования к системам управления большинства пром концернов.

И могу на них ссылаться. При желании

Ну конечно! И даже можете доказать, что на CPU общего назначения можно добиться латентности при реакции на внешнее событие в пределах десятка наносекунд, как, например, на таком SoC? )))

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

А какие процессоры стоят в Плк, можете поискать. Обычные там.

Ну и вообще, ттх такого оборудования в общем доступе

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

При чем тут наносекунды процессора, если в данном случае речь о латентности FPGA Cyclone под управлением процессора?

А какие процессоры стоят в Плк, можете поискать

Ага. Например, Siemens S7-1500 с TM FAST Modules, которые явно на FPGA.

При чем тут модуль когда речь шла о цпу.

CPU в таких случаях лишь программирует FPGA. Особой производительности от него тут не требуется.

Где это? Я изначально веду речь о MK и SoC, где CPU - лишь один из компонентов.

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

Не туда смотрите. Надо сюда.

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

Потому что производительность подобных SoC определяется в первую очередь FPGA, а не CPU.

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

пердюшку за 10 баксов

Вы ошиблись на три порядка. Такие SoC стоят десятки тысяч долларов.

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

То, что выделено синеньким - это ссылка. Откройте и посмотрите на цену.

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

А почему вы дали ссылку на Altera 100G, а не на, например, Porsche 911?

Чтобы Вы увидели цены на топовые SoC.

вашему изначальному тезису

Потому что Вы не умеете читать больше одного предложения )))
"на любом CPU без аппаратной поддержки виртуальной памяти"

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

"надо обязательно всегда" - никто кроме тебя этого не утверждал. Это защита от дурака. Не помогла.

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

Учимся пользоваться отладчиком

Брейкпойнт на данные? Условный? И это всё ради того чтоб не писать ненавистный сеттер?? А что если значение неизвестно? А если экземпляров класса много?

Займитесь этими вопросами в качестве самостоятельной работы.

понятно.

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

Да и вообще, в раст не завезли ооп, потому что его не завезли в го.

ООП в Rust было, но задолго до 1.0. У него были классы: источник. В целом, Rust 1.0 отличается очень сильно от Rust Beta, там поломали почти всё.

Плюс ещё процитирую человека ниже:

Этому тезису никак не мешает то, что раст начали пилить в 2006 (ради Servo, но кто теперь вспомнит), а Go — в 2007?

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

Да, так и есть ;)

Делается приватное поле с геттерами и сеттеоами вместо публичного, чтобы в будущем при появлении логики при гете/сете api (и abi!) не поменялось.

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

Возьмем классическое "беспроблемное" наследование много-от-одного. Структура на 5 полей, реализация на 15 методов. В одном наследнике переопределяется метод 7, в другом 7 и 9, в третьем 8, 9 и добавляется 16 "служебный". Какой-то из них может добавить поле в состояние. Другой может стать предком для еще одного наследника. Переопределение какого-то метода, лишь добавление всего одной операции до или после метода предка.

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

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации