Комментарии 138
Всегда умиляли эти теоретики от программирования, которые не закончили ни одного проекта.
Всегда хочется спросить - "когда ты написал свою миллионную LOC?"
Автор не упоминает LINQ и ему подобных, которые стали частью языка.
Дополню в ту же тему
Проблема Х
Все преимущества Раста показываются на примерах уровня хелловорлда - пара строк кода. И эта статья не исключение.
А если мы начинаем смотреть реальные проекты на Раст - "там водятся тигры" (С)
Я не говорю, что Раст совсем плохой - наоборот, в нем многое сделано хорошо (да, но далеко не все). Но показать это надо правильно - на реальных задачах.
Я про след ФП в других языках расскажу в следующей главе ;)
F#, Haskell, Lisp, Scala — все они уступают Rust в скорости и потреблении ресурсов.
Citation needed. Да и в целом, «язык А быстрее языка Б, вот и пластмассовые бенчмарки подоспели» — это либо глупость, либо вредительство. Если вагон задач, в которых любой из вышеперечисленных языков уделает раст, как бык — овцу.
Раньше ФП ругали за медлительность. «Это не для серьёзных задач», «это только для прототипов».
Citation needed. Да и в целом, хаскель ругают за одно, а скалу — за совсем другое.
[…] попробовать функциональный Rust. Без
mut.
Совет прям от Григория Остера. А как насчет стейта в асинхронных тредах? В базу сохранять?
Не, я понимаю, что ваш подход прекрасно работает на примерах в три строки из туториала. Но вы тут заикались про продакшн, а там, знаете, бывает хайлоад, большая конкурентность, долгоживущие треды, и всякая другая непотребщина.
Не нужно тащить в джаву лямбды, в джаваскрипит классы, в раст — иммутабельность, пожалуйста. Возьмите подходящий вашим препочтениям язык (функциональных иммутабельных языков — полно́) — и пишите на нем. А в расте даже акторная модель через жопу сбоку прикручена.
Проблема II: Императивность
Вот тут не согласен с предложенным решением. Имхо плодить пятистрочные функции по типу bring_beer это вредный подход. Это выглядит красиво, не несёт никакой практической пользы, и в будущем усложняет отладку и понимание кода в целом. Я ещё могу понять, если это делается в нескольких местах сразу (но в таком случай любой, кто слышал про DRY выделит это в функцию, и про такое в статье можно и не писать). А делать пятистрочные функции, которые вызываются ровно в одном месте - это просто портить опыт чтения кода сверху-вниз, когда приходится прыгать куда-то там, где объявлена эта маленькая функция.
Проблема III: Некорректная или недостаточная обработка ошибок.
Здесь я бы упомянул, что неожиданно может оказаться, что вызываемая функция паникует, и это будет такой же тостер, сжигающий дом. Но nopanic это отдельная банка червей, как говорится.
Проблема исключений № 1: Вы не знаете, что именно может упасть.
В расте есть такая же проблема, хоть и в меньшей степени. Вызываемая функция может неожиданно запаниковать, как я уже выше писал.
Немного ошарашивает ваш религиозный фанатизм в отношении функционального программирования и поливание помоями императивного. Вам бы попросить чажпт (или с чьей помощью вы пишете) подуспокоится. Но может мне просто такой стиль не заходит :)
И немного раздражает вот этот подход с аналогиями, где каждый пункт начинается с “представьте…”. Ей богу, вы не для детсадовцев пишете, а для инженеров, тут не надо на грушах и яблоках объяснять.
В расте есть такая же проблема, хоть и в меньшей степени. Вызываемая функция может неожиданно запаниковать, как я уже выше писал.
Это не совсем так, #[no_panic] может в рамках крейта доказывать, что указанная функции не паникует. А при сборке с LTO, и с учётом других крейтов (транзитивно).
Ну так это сторонняя библиотека и вроде как у неё есть какие-то заковырки? В любом случае не часть стандартной поставки языка
Ну и ничего из перечисленного не является какой-то специфической особенностью именно вот функционального стиля.
Обработка ошибок путём явного возврата их по значению в 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 данных. В случае с функцией параметры в неё пропихиваются вызывающей стороной, у вызывающего нет никакой возможности не передать параметр, у него не получится забыть это сделать. А у функции нет необходимости куда-то идти за значениями, они в аргументах. Также у вызывающего функцию нет необходимости куда-то идти за результатом вызова, он не доступен никак иначе, кроме как значение, получаемое в месте вызова. Иммутабельность - это следствие.
Исключительно из любви к формальности:
foo :: Integer
foo = undefinedХаскелисты традиционно притворяются, что undefined (и прочих нетотальностей) не существует, и вообще что Hask — настоящая категория.
Олсо, если подходить формально, то
ФП по умолчанию решает эту проблему: значение может быть пустым только явно — с помощью
Option<T>
имеет некоторый смысл даже в нетотальных языках при наличии undefined, error и прочих подобных: эти «пустые» значения невозможно наблюдать в чистой части программы в рамках стандартной метатеории [разных вершин] лямбда-куба, без компиляторной магии (ST/IO — компиляторная магия, seq — компиляторная магия, да и она тоже не меняет большую операционную семантику, потому что сходящиеся термы что с seq, что без seq имеют одну и ту же нормальную форму).
Иными словами, ты в чистом коде не можешь написать
isBottom :: Int → Bool
isBottom n = n == ?⊥?поэтому про наличие пустоты ты не узнаешь.
Аргумент $dSpeak это указатель на структуру Speak a, где лежит функция speak. Ничего не мономорфизировалось, производительность падает так же, как в ООП языках.
После инлайнинга и/или при наличии доступного анфолдинга в использующем модуле оно вполне может специализироваться, если компилятор решит, что это выгодно (я про это целый пост писал пару лет назад!)
И как в расте, при использовании dyn Trait
Для этого в хаскеле тоже надо явно обмазываться экзистенциальными типами, ну либо убить оптимизатор (что можно сделать в любом языке).
Как с помощью
IOпронаблюдать понятно, а как можно с помощьюSTилиseq?
ST — это обобщённый IO (в прямом смысле, IO ≅ ST RealWorld).
seq — так, для полноты. Его расходящийся результат тоже ненаблюдаем в этом же смысле.
Так-то и в объекто-ориентированных языках вызовы девиртуализироваться могут, но полагаться на оптимизатор ни в ООП, ни в ФП, имхо, не стоит.
Только для девиртуализации компилятору надо доказать, что в данном месте других инстансов быть не может, а это задача сильно более нелокальная и сильно более сложная, чем просто подставить параметр, когда он доступен (что выполняется субъективно ИМХО куда чаще, чем условия для девиртуализации, потому что мало кто играется с экзистенциальными типами или полиморфной рекурсией и при этом ожидает должный перф).
Особенно как автор статьи, заявляющий, что вообще всё мономорфизируется.
Это он зря, конечно.
А не осталась, случайно, ссылка?
Последний пост в моём стандалон-блоге.
А ты знаешь, где обсуждается проблема нелокальности ленивости? То есть, как с этой нелокальностью бороться, да и вообще делается «постновка задачи».
Где обсуждается — не знаю. Но решение как в идрисе (когда ленивость явно помечается как Lazy) выглядит вполне себе дружественным к тайпчекингу и локальным рассуждениям. Ты, по крайней мере, знаешь, что именно у тебя ленивое.
Спасибо!
Я хотел чуть раскрыть тут мысль - явные thunk'и, кмк, являются слишком низкоуровневым средством. Это как ассемблер по сравнению со структурным программированием. То есть, не очень хватает тут экспрессивности текущих языков.
С другой стороны, ленивость - это уже имеет отношение к вычислительной сложности (и по памяти тоже), а эту сложность вообще не очень умеют описывать, кмк. Нет языков, где она прописывается явно.
А что именно ты хочешь выражать? Сложность — другой разговор, именно что более общий.
И пару слов про microHS черкануть в формате поста можешь?
1.
В самих по себе мутациях нет ничего плохого. Проблемы начинаются тогда, когда они становятся нелокальными.
А я разве так в статье не сказал? ;)
Если уж совсем никак — мутируйте, но не позволяйте мутабельности распространяться на весь код. Изолируйте её в рамках одного блока.
2.
Вы умудрились попасть в едва ли не единственную фичу тайпскрипта, которая доживает до рантайма: Enums are real objects that exist at runtime (source)
Про вот это не знал, спасибо!
Ох ты ж блин - статья является отличной иллюстрацией тезиса: "Человек, впервые взявший в руки молоток - рассматривает все окружающие предметы как разновидности гвоздей" (С).
Да, мы все в курсе, что исчисление функций эквивалентно машине Тьюринга - а значит любая вычислимая задача может быть описана как функционально, так и императивно. То есть - даже мой поход в магазин в субботу можно описать как функциональное преобразование из цифр на банковском счете в набор продуктов в холодильнике...
Вопрос - нахрена, а главное - зачем ?! Если объективно процесс ествественным образом описывается как набор шагов, меняющих состояние системы - то почему бы не последовать совету берестяной грамоты N35, и не описать его на языке программирования тем же (т.е. императивным) способом ? В чем смысл натягивания совы на глобус и вытягивания цепочки лямбд - которые потом еще хрен отладишь нормально (потому что не видны промежуточные преобразования коллекций) ?
Я лично вижу два случая, когда ФП надо применять:
У вас есть большой и полезный фреймворк, который вы можете кастомизировать - и для этого вам надо в глубины этого фреймворка передать определенное поведение. Так вы вот это поведение запихиваете в функцию, и ее как first-class-object передаете в глубины чужого кода. В моем примере с магазином - это как если бы кто-то уже написал фреймворк, автоматизирующий все промежуточные этапы, и надо было только подать внутрь функцию выбора товаров с полок...
Либо у вас функциональное описание является естественным описанием процесса. То есть реально процесс зависит только от входов, не порождает side-effects (логгинг и инструментация весело машут ручкой в этот момент - ибо являются сайд-эффектами), и т.д.
Ну и до кучи скажу что архитектура современных ЭВМ нихрена не заточена под ФП - она заточена под императивное программирование с мутабельным состоянием в RAM. Соответственно, ФП всегда будет подвергаться performance penalty по сравнению с императивным кодом. Другое дело - что производительность софта за последние десятилетия опустили на такое дно, что добавление туда еще и ФП - уже существенно ничего не меняет...
То есть - даже мой поход в магазин в субботу можно описать как функциональное преобразование из цифр на банковском счете в набор продуктов в холодильнике… Вопрос - нахрена, а главное - зачем ?! Если объективно процесс ествественным образом описывается как набор шагов, меняющих состояние системы
Даже процесс похода в магазин вы внутри себя описываете декларативно. Вы думаете в терминах того, что надо сделать (выйти из квартиры, вызвать лифт, перейти дорогу, дойти до магазина), а не как это сделать (это будет настолько объёмно, что я даже проверку открытия-закрытия двери писать не буду), и вы притворяетесь, что у ваших действий нет несмоделированных сайд-эффектов: вы не рассматриваете износ петель двери в процессе выхода из дома, выхлопные газы автобуса, смену настроения продавщицы в магазине из-за рисунка на вашей кредитной карточке, и так далее.
Вопрос - нахрена, а главное - зачем ?!
Потому что о чистой функциональщине без неявных сайд-эффектов проще рассуждать и доказывать её свойства.
В чем смысл натягивания совы на глобус и вытягивания цепочки лямбд - которые потом еще хрен отладишь нормально (потому что не видны промежуточные преобразования коллекций) ?
Прелесть в том, что их не нужно отлаживать так часто, как вы думаете :]
Ну и до кучи скажу что архитектура современных ЭВМ нихрена не заточена под ФП - она заточена под императивное программирование с мутабельным состоянием в RAM. Соответственно, ФП всегда будет подвергаться performance penalty по сравнению с императивным кодом.
А почему у меня тогда на хаскеле регулярно получается писать код по производительности на уровне плюсов (иногда — быстрее)?
Потому что нет, конечно, не «всегда будет», это non sequitur. Компилятору ФП никто не мешает компилировать ФП-код во вполне производительный императивный мутабельный код. Более того, начиная с некоторого уровня сложности компиляторов это проще: типизированное ФП (а другое не нужно) выражает в типах больше инвариантов и даёт компилятору больше информации о том, какие оптимизации не изменят семантику программу, и их можно безопасно применить. Просто до этого уровня в императивном (и близком к железу — привет условной джаве) программировании программист делает ту работу, которую может (и должен) делать компилятор.
А вообще забавно то, что я как раз на неделе начал читать сборник публикаций первой конференции History of Programming Languages, и там целый Джон Бакус (который автор Фортрана, куда уж императивнее, и который Backus Normal Form для описания синтаксиса) пишет где-то в окрестности 1980-го года:
My own opinion as to the effect of FORTRAN on later languages and the collective impact of such languages on programming generally is not a popular opinion. […] I now regard all conventional languages (e.g. , the FORTRANs, the ALGOLs, their successors and derivatives) as increasingly complex elaborations of the style of programming dictated by the von Neumann computer. These " von Neumann languages" create enormous, unnecessary intellectual roadblocks in thinking about programs and in creating the higher level combining forms required in a really powerful programming methodology. Von Neumann languages constantly keep our noses pressed in the dirt of address computation and the separate computation of single words, whereas we should be focusing on the form and content of the overall result we are trying to produce. We have come to regard the DO, FOR, WHILE statements and the like as powerful tools, whereas they are in fact weak palliatives that are necessary to make the primitive von Neuman style of programming viable at all.
By splitting programming into a world of expressions on the one hand and a world of statements on the other, von Neumann languages prevent the effective use of higher level combining forms; the lack of the latter makes the definitional capabilities of von Neumann languages so weak that most of their important features cannot be defined-starting with a small, elegant framework-but must be built into the framework of the language at the outset. The gargantuan size of recent von Neumann languages is eloquent proof of their inability to define new constructs: for no one would build in so many complex features if they could be defined and would fit into the existing framework later on.
The world of expressions has some elegant and useful mathematical properties whereas the world of statements is a disorderly one, without useful mathematical properties. Structured programming can be viewed as a modest effort to introduce a small amount of order into the chaotic world of statements. The work of Hoare ( 1 969), Dijkstra ( 1 976), and others to axiomatize the properties of the statement world can be viewed as a valiant and effective effort to be precise about those properties, ungainly as they may be.
This is not the place for me to elaborate any further my views about von Neumann languages. My point is this: while it was perhaps natural and inevitable that languages like FORTRAN and its successors should have developed out of the concept of the von Neumann computer as they did, the fact that such languages have dominated our thinking for twenty years is unfortunate. It is unfortunate because their long-standing familiarity will make it hard for us to understand and adopt new programming styles which one day will offer far greater intellectual and computational power.
Вот вы удивитесь - но если я кому-то буду рассказывать как я хожу в магазин - то это будет именно пошаговое перечисление действий: одеваюсь и оказываюсь одет, выхожу и оказываюсь на улице, двигаюсь в разных направлениях по улицам и перекресткам - оказываюсь у дверей магазина, набираю продукты, расплачиваюсь на кассе, и т.д. А вот сайд-эффектами этой деятельности оказываются a) уменьшившееся количество денег на счете и b) увеличившееся количество продуктов в холодильнике. И да, можно дискутировать о том до какой степени детализовать этот процесс (в программировании мы обычно детализируем до stdlib call или syscall). Без шуток - покажите мне человека который будет описывать поход в магазин как функциональное преобразование цифр в продукты... "А папа-мама не сумасшедши ли ?" (C) Мультик
Дальше - аргументы о том, что проще рассуждать и доказывать. Охотно верю - но знаете, идите на матфак работать с такими аргументами, что-ли ? Там любят таких... А я тут у мамы инженер. Мне не надо чтобы красиво доказано - мне надо чтобы работало, и легко читалось. Понятно что когда у меня есть streams, то ими надо пользоваться - например не писать свой алгоритм сортировки, а передать лямбду в библиотечный sort(). А когда один черт в коде клеится весь алгоритм, но пересыпается .apply() .with() .orElse() и прочей матерщиной (привет Kotlin!) - очень хочется дать по башке, и отправить копию новгородской грамоты чтобы лучше доходило...
Аргумент про то, чтобы компилировать ФП в мутабельный код - это из серии "можно, но зачем?!". То есть сначала мы сношаем мозг чтобы императивный процесс реального мира описать эквивалентом в мире чистых функций, а потом ждем чтобы компилятор обратно превратил это в императивный код с мутабельными объектами... Я даже теряюсь что предложить в таком случае... Например, когда в следующий раз захотите в туалет - не идите сразу у себя дома, а не поленитесь и дойдите пешком до центра. Потом такси вас обратно привезет - и только потом на толчок... После эксперимента задайте себе вопрос - стоило ли оно того, чтобы оказаться в том же месте по такой кружной траектории ?
Сравнение желтого с вонючим. Кмк любому очевидно, что писать императивный код человеком и трансформировать математически доказанный код в императивный самой машиной - это две большие разницы.
Вот вы удивитесь - но если я кому-то буду рассказывать как я хожу в магазин - то это будет именно пошаговое перечисление действий: одеваюсь и оказываюсь одет
Это пошаговое описание именно в декларативном стиле. Ровно в том же смысле, как и, возвращаясь к программированию, «получить множество авторов статей в хабе “программирование” можно через nubOrd . map author . filter (λpost → "программирование" `elem` hubs post)». Вы можете переубедить меня, описав процесс надевания куртки в императивном стиле, с мутабельными переменными, циклами, явными условиями, и так далее. Чем меньше абстракций уровня «просовываю руку в рукав», тем ближе к императивщине.
Практически все попытки выше ассемблера (включая макроассемблер), все эти функции, ООП, структурное программирование, прочее — это попытки разной степени успешности выйти из императивно-детализированной концепции в декларативную. Эти все вот ООП-шаблоны, билдеры там, фабрики, стратегии — это попытка эмулировать декларативщину в императивной среде.
Без шуток - покажите мне человека который будет описывать поход в магазин как функциональное преобразование цифр в продукты… “А папа-мама не сумасшедши ли ?”
Сами придумали тезис («ФП/декларативщина — это когда поход в магазин представляется преобразованием цифр в продукты»), сами его оспариваете, сами победили. Как это называется? Это называется strawman argument.
Олсо, это ведь работает во все стороны: сколько людей описывает поход в магазин как ООП? Сколько людей… инстанциирует класс бумажника? обращается к синглтону бумажников? использует абстрактный класс ИбуМажник? Чей метод вызывается при обмене денег на товар — бумажника, денег, товара, продавца, или абстрактного менеджера сделок? Вы когда ребёнку будете объяснять процесс похода в магазин, будете рассказывать про абстрактный менеджер сделок?
Можно ли сделать вывод в вашем мире, что ООП — это академический зашквар?
Охотно верю - но знаете, идите на матфак работать с такими аргументами, что-ли ? Там любят таких… А я тут у мамы инженер.
Я нигде не говорил о красоте доказательства, я говорил о простоте и возможности. Императивный код особо не доказывают, там херак-херак и в продакшен обычно. Это да, это в императивщине и нетипизированной функциональщине делать проще.
Но я как-то всё-таки хотел бы использовать слово «инженер» как похвалу, а не как уничижительный термин.
Мне не надо чтобы красиво доказано - мне надо чтобы работало, и легко читалось.
И это быстрее происходит в типизированной функциональщине.
А когда один черт в коде клеится весь алгоритм, но пересыпается .apply() .with() .orElse() и прочей матерщиной (привет Kotlin!) - очень хочется дать по башке, и отправить копию новгородской грамоты чтобы лучше доходило…
Товарищ взял не предназначенный для ФП язык и удивляется, что ФП выглядит в нём паршиво. Ну, я не знаю, попробуйте в ООП-стиле на ассемблере писать и потом ругайтесь, что ерунда выходит, клятый ООП.
Аргумент про то, чтобы компилировать ФП в мутабельный код - это из серии “можно, но зачем?!”.
Оптимизирующие компиляторы вы не приемлите вообще, я так понимаю, или это только применительно к ФП такое неприятие?
Ну как бы если любое движение от ассемблера считать движением в сторону ФП - то дальше спор смысла не имеет. Просто живите там у себя в мире математических абстракций, и даст бог в реальной жизни я вас не встречу.
Я видел развитие языков программирования через руки - начиная от 8-бит ассемблера, и дальше через 80x86 к C, C++, Java и так далее. Никакого движения в сторону FP там не было. Есть объекты реального мира. У них объективно есть состояние, которое меняется со временем по присущим им законам - или под внешним воздействием. В программе мы строим модель этого объекта или явления, отображая законы реального мира в их подобия в модельном пространстве. То-есть заменяем реальный вес на последовательность битов, его кодирующую (с фиксированной или плавающей точкой), положение объекта в пространстве на значения эйлеровых углов (или чего-то посложнее), и так далее. На низком уровне (ассемблере) - не существует способов выразить в языке структуру модели - какая ячейка или регистр за что отвечает - приходится держать в голове. Все дальнейшие нормальные языки: от прости господи, бейсика - до джавы - вводили разные изобратительные средства чтобы отразить объективно существующую структуру реального мира в программе с минимальными искажениями. То есть - группировать признаки относящиеся к одному модельному объекту в структуры, и сделать сложнее некорректные действия - типа вызвать метод "покрутить хвостом" к кухонной двери вместо кошки. Никто, щука, никогда не пытался представить модель мира как комбинацию бесчисленного количества чистых функций.
При том, что интеллектуально я готов принять множественность описания объектов реального мира (например, описание сигналов в частотной области спектрами, или во временной - графиками) - идея описать всё сущее чистыми функциями - проходит в моей канцелярии как забавный курьез. Ну и что, что можно ?! Вон метафора конечных автоматов тоже полна по Тьюирнгу - и любой процесс или явление можно описать таким образом. И есть чудаки которые до сих пор носятся с концепцией автоматного программирования. И аргументы у них точно такие же как у вас: это простой формализм, компилятору легче оптимизировать, и т.д. Проблема в том, что существует все-таки естественное описание объекта, которое является предпочтительным. Есть природные явления которые прямо-таки созданы для описания КА (или ФП). И там их надо применять. А там где это не естественно - там начинаются натягивания совы на глобус: давайте создадим скрытые фиктивные состояния, давайте добавим фиктивные входные и выходные сигналы, и т.д.
В итоге - повторю свой изначальный тезис: то что у вас появился в руках молоток - не делает остальные предметы гвоздями! Пожалуйста не надо хреначить направо и налево - да еще и похваляться этим...
Любая абстракция лишь примитивное упрощение реальности мира.
В этом смысле они равноудалены и не особо лучше одна другой)
Ну как бы если любое движение от ассемблера считать движением в сторону ФП - то дальше спор смысла не имеет.
Я же явно написал «попытки разной степени успешности выйти из императивно-детализированной концепции в декларативную» (в прошлый раз без жирного, но, может, вы в этот раз заметите?). То есть, в сторону повышения способов выразить, что надо делать, а не как. Потому что когда вы пишете в своём императивном ООП animal.makeSound(), вы описываете, что надо сделать. Читатель этого кода не знает деталей реализации makeSound, процесса вывода байтиков на экране и смены состояния TTY, конкретного животного, да и особенностей работы виртуальных функций вообще и в данном конкретном случае тоже знать не обязан.
Но в сторону ФП тоже движение есть, что самое смешное, хотя я об этом не говорил! Оно настолько общепринято, что говорить об ФП в стиле 50-х, в смысле лиспа и «уау функции высшего порядка», в 2026-м не имеет смысла: это есть в подавляющем большинстве языков. И движение туда происходит в самых-самых мейнстримных иимперативных языках, начиная от евнух-версии в виде указателей на функции в C, и заканчивая делегатами в сишарпе, функторами в C++98 и целым STL, построенным на «в функцию-алгоритм передаётся произвольная функция», лямбдами в C++11, етц.
Никакого движения в сторону FP там не было.
kekw
Есть объекты реального мира. У них объективно есть состояние, которое меняется со временем по присущим им законам - или под внешним воздействием.
Или нету никаких изменений, а есть только функция s = s(x⃗, t).
Вы ещё не свыклись с мыслью, что вы просто живёте в четырёхмерном пространстве-времени (реальнее некуда)? А этой мысли уже больше ста лет.
Никто, щука, никогда не пытался представить модель мира как комбинацию бесчисленного количества чистых функций.
Смысл не в чистоте функций, а в том, что в типе функций указаны её эффекты.
Мастурбация на чистоту — это действительно молоток в руках вчера его освоившего.
Олсо, это ведь работает во все стороны: сколько людей описывает поход в магазин как ООП? Сколько людей… инстанциирует класс бумажника? обращается к синглтону бумажников? использует абстрактный класс ИбуМажник? Чей метод вызывается при обмене денег на товар — бумажника, денег, товара, продавца, или абстрактного менеджера сделок? Вы когда ребёнку будете объяснять процесс похода в магазин, будете рассказывать про абстрактный менеджер сделок?
Ха-ха-ха 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, а нам ближе классовый подход (это шутка еслив что)
Символическое в ООП — более воображаемое и нарративное. Оно создаёт красивую историю: «объекты взаимодействуют, как люди/вещи в мире».
Только для программирования нужна точность, а не красота и нарративность. Красота и нарративность хорошо подходят для книжки, почитать на ночь там что-нибудь этакое.
Это мощно, но когнитивно дороже для большинства.
Блин, я даже не знаю, какой вариант ответа на это выбрать:
Большинство людей не может создавать работающие программные системы, это просто медицинский факт. Когда мы пытаемся идти против этого факта, то получаем жрущий полтора гигабайта рамы у меня сейчас gmail, тормозящее дерьмо на электроне, приложения сбера на гигабайты, и регулярно сломанную форму и страницу комментариев на хабре.
Неужели держать в уме всю сеть взаимодействий между разными объектами и их методами когнитивно проще, чем когда побольше ограничений выражено в типах, и три четверти багов устраняется ещё при написании, даже до сохранения файла на диск, рефакторинг простой и понятный, и контрибьютить в очень нетривиальные кодовые базы можно на второй день?
Впрочем, эти варианты не являются взаимно противоречащими, поэтому можно выбрать их оба сразу. Пожалуй, так и поступлю!
Моя голова устает сильнее когда я программирую на ФП.
Моя голова устаёт, когда надо разбираться в ООП-лапше, потому что меняешь что-то здесь, а разваливается там.
А в случае ФП я могу прийти в новый нетривиальный проект вроде компилятора и продуктивно в него контрибьютить в первый же день. В худшем случае, в случае написанного в ФП-стиле кода на императивном языке, какого-нибудь там HFT-пайплайна на плюсах — в первую неделю.
А онбоардинг в ООП-проект может занимать месяц-другой, даже если его intrinsic-сложность на порядки меньше, и это не компилятор целого языка с тайпчекером, оптимизациями и доказательством корректности учёта газа (привет блокчейн), а, ну, не знаю, абсолютно простая хрень вроде «получи новость от Reuters и ещё десятка источников, сделай из неё bag-of-words-кодировку, посчитай расстояние Хэмминга с центрами имеющихся кластеров, найди ближайший, обнови». Это 500 строк на хаскеле в худшем случае, которые в ООП-стиле превращаются в 50 тыщ макаронов.
Есть такой чел 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)?
акторы ортогональны фп и ооп
Серьёзно? Может, и
electron.quantumPhaseTransition()— интуитивно понятнее, чемquantumPhaseTransition(electron)?
электронный_газ = QuantumSystem([electron1, electron2, ....electron99])электронный_газ.перейти_в_основное_состояние()электронный_газ.PhaseTransition()
QuantumSystem может и из одного электрона состоять — квантовый фазовый переход тут невозможен.
Вам нужен синглтон объект — аналог вселенной в которой все происходит. Если у вас QuantumState еще во что-то вложен, то следующий в иерархии синглтон объект. Если вам нужны статистические свойства, то синглтон объекты инстанцируются в разных не взаимодействующих потоках или последовательно в одном потоке.
потому что Haskell годен только для красивых журнальных статей и всяких бла-бла-блашек типа Learn Quantum Mechanics with Haskell.
Походу надо пойти вернуть деньги людям, платившим мне за работу на хаскеле.
Основная проблема с утверждениями вроде вашего в таких тредах: их невозможно проверить и опровергнуть (ссылки на достаточно крупные проекты вроде тех же блокчейн-сред, или использование хаскеля в банке Mercury, или использование диалекта хаскеля в Standard Chartered, или использование хаскеля в фейсбуке, или использование хаскеля для верификации seL4, или да мало ли, будут отвергнуты, конечно, с той или иной формой сдвига гоалпостов). Но такие тезисы, как ваш, впрочем, и не предназначены для передачи информации о наблюдаемом мире: они предназначены для поддержания дискурсного консенсуса.
Но чаще этот приём используется в политических дискуссиях, конечно.
Я тоже самое могу на Python + Numba написать с помощью готовых boxed-библиотек типа Numpy и будет работать не сильно медленнее Fortran (правда памяти жрет как не в себя, но это скипнем).
А сколько ООП там будет? Будете каждому числу в матрице посылать сообщение «умножь себя на вон то и сложись с аккумулятором», как Кей завещал, или таки будет что-то адекватное?
ООП у вас там будет использоваться только как довольно хреновая замена системе модулей и неймспейсов.
Зато у них есть оригинальные бизнес идеи или идеи по квантовой химии, которые большинство программистов не поймет, этот тоже факт.
Ещё раз, мой тезис: большинство программистов не способно создавать программные системы. И да, это же большинство неспособно разобраться в квантовой химии, да и с хорошими бизнес-идеями (выше уровня пельмешек и когтеточек) там будет туговато, боюсь, но в бизнесе не шарю уже я, чтобы говорить про это конструктивно. Ваш тезис не противоречит моему, более того, они в каком-то смысле друг друга подтверждают.
И ООП помогает наладить диалог между ними лучше чем ФП.
Нет, конечно, не помогает. Помогает domain-driven design, который в ФП реализуется куда проще и понятнее, чем в ООП.
Электрон подкласса Элементарная частица интуитивно ясен, а для ФП нужно целую статью писать "как выучить квантовую механику на Хаскеле".
А потом у вас будет синглтон-менеджер фабрик элементарных частиц. Очень понятно.
Содержимое учебников по квантам спокойно перекладывается на функциональный стиль по построению: есть данные (состояние частиц), на них действуют какие-то функции, получаются новые состояния. Перекладывать это всё в ООП… не, спасибо, я лучше слесарем сортиры прочищать пойду.
Содержимое учебников по квантам спокойно перекладывается на функциональный стиль по построению: есть данные (состояние частиц), на них действуют какие-то функции, получаются новые состояния.
Это "функции" называются операторами и они линейные — из этого следуют всякие финтифлюшки, типа 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. Вопрос о стиле программирования просто не возникает.
Это "функции" называются операторами и они линейные — из этого следуют всякие финтифлюшки, типа no-cloning theorem.
Оператор — это функция (о, ФП!) с особых образом ограниченным (о, типы!) доменом, кодоменом, и ограничениями (больше типов!) на собственное поведение. Здесь нет нужды в ООП.
Во всём остальном, о чём вы пишете, тоже нет нужды в ООП на уровне задачи.
Поэтом алгоритм нахождения собственных значений квантового оператора допускает чисто функциональную спецификацию
Более того, это единственная естественная спецификация.
но не допускает чисто функциональную реализацию: стоимость персистентности состояния превышает доступные ресурсы.
Вы тут тоже, как и предыдущий оратор, боретесь с соломенными чучелами: во-первых, смысл обсуждаемой функциональщины не в отсутствии всяких сайд-эффектов, а в их выражении в типах. Во-вторых, компилятору никто не мешает заметить, что предыдущая версия структуры не используется, и свести реализацию к инплейс-апдейтам. В-третьих, вы что-нибудь слышали про линейные типы?
а тут только суровое ООП
Почему?
(даже нашел непопсовый опенсосрс).
Открыл произвольный файл, попал на это. Ну, чтобы далеко не ходить, публичный API «класса» выглядит так:
class CCEnergyWavefunction : public Wavefunction {
public:
CCEnergyWavefunction(std::shared_ptr<Wavefunction> reference_wavefunction, Options &options);
~CCEnergyWavefunction() override;
double compute_energy() override;
std::map<std::string, SharedMatrix> get_amplitudes();
// (index within irrep, irrep) -> global index
std::map<std::tuple<int, int>, int> total_indices;
};Что это такое? Это тупо функция
ccEnergyWaveFunction
:: Wavefunction
-> Options
-> (Double, Map String Matrix)
-- неленивые могут заменить возвращаемый тип на
data ComputedCCWV = ComputedCCWV
{ energy :: Double
, amplitudes :: Map String Matrix
}Всё остальное в этом классе — как в той шутке, замыкания для бедных.
Может, Wavefunction для полиморфизма используется? Ну, класс переопределяет только compute_energy, который не используется в базовом классе, поэтому не кастомайзит поведение, поэтому это, опять же, просто реюз кода и выражение идеи «у меня тут есть функция с возвращаемым параметром Double».
Тем временем базовый класс:
// TODO: pass Options object to constructor instead of relying on globals
Wavefunction::Wavefunction(std::shared_ptr<Molecule> molecule, std::shared_ptr<BasisSet> basisset,
std::map<std::string, std::shared_ptr<Matrix>> matrices,
std::map<std::string, std::shared_ptr<Vector>> vectors,
std::map<std::string, Dimension> dimensions, std::map<std::string, int> ints,
std::map<std::string, std::string> strings, std::map<std::string, bool> booleans,
std::map<std::string, double> floats)Господи, как же это всё тошнотворно. У меня ПТСР от этой фигни вообще, потому что лет 10 назад один мой рабочий проект сводился к тому, чтобы переписать такой написанный «учёными» (аспирантом по матстатам, проходившим стажировку в компании и написавшим прототип детектора аномалий во временных рядах) код, написанный в ровно том же стиле: один класс с .cpp-файлом на несколько тыщ строк, который по факту является скоупом для всей программы (использовать глобальные переменные было бы честнее), и где всё взаимодействие между разными частями кода происходит через один std::map<std::string, std::string> , типа theMap["sigmaCoeff3_after2"] = std::to_string(computedSigma);, ну и потом парсить обратно. Ну, в этом psi4 хотя бы значения не теряют типы — видимо, код писали не аспиранты, а постдоки, а, может, и целые tenured-профессора.
Тот проект до меня, кстати, пытались переписать двое, но не смогли разобраться в хитросплетениях потока данных. Я за несколько календарных месяцев тоже не смог, поэтому решил, что это дело гиблое, и подошёл к коду как к чёрному ящику: накидал в него разных сгенерированных временных рядов, посмотрел, где оно находит аномалии, потом сделал сам пару алгоритмов (ну там, gauss changepoint detection, всякое такое), посмотрел, какой из них ближе всего к исходной реализации и к здравому смыслу, и просто заменил этим алгоритмом.
Во-вторых, компилятору никто не мешает заметить, что предыдущая версия структуры не используется, и свести реализацию к инплейс-апдейтам. В-третьих, вы что-нибудь слышали про линейные типы?
Постоянно! При свёртке нескольких тензоров порядок операций критически влияет на сложность:
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 нет. Вот так и живем — на словах в ФП все красиво.
Эту задачу решает ООП Daniel G. A. Smith opt_einsum, потому что ФП не смог, не смотря на все его типы.
Где не смог и почему? Потому что Daniel G. A. Smith решил выбрать не ФП-язык?
Это задача на упрощение AST. AST выражения с тензорами строится настолько просто в ML-языках, что об этом неприлично говорить:
data Expr
= Leaf Mat
| Mul Expr Expr
| Add Expr Expr
| Transpose Expr
| ...
class ExprLike a where
toExpr :: a -> Expr
instance ExprLike Expr where
toExpr = id
instance ExprLike Mat where
toExpr = Leaf
(:*:) :: (ExprLike a, ExprLike b) => a -> b -> Expr
a :*: b = toExpr a `Mul` toExpr b
(:+:) :: (ExprLike a, ExprLike b) => a -> b -> Expr
...
prepare :: Expr -> Expr
prepare = _ваш_алгоритм_упрощенияВ плюсах это называется expression templates (и не требует ООП, к слову, но требует нетривиальных телодвижений, зато где-то в окрестности ранних нулевых было поводом говорить «плюсы в лице blitz++ уделывает фортран!»), в джаве наверняка есть какой-нибудь паттерн про это (который, конечно, требует ООП, но в джаве и хелловорлд не напишешь без ООП).
приходится вот так ему помочь, через вызов методов в нужном порядке
Не понял, отсос ФП и помощь ФП заключается в том, что в (якобы) ООП-коде этому ООП-коду нужно руками указывать порядок вычислений?
Скрытый текст

Мне один tenured-профессора из Кембриджа рассказывал, как ему один программист «переписал» на ФП, после чего программа стала работать в 100 раз медленнее )))
Тож аспирант небось? Это норма: аспиранты почему-то не умеют писать код. Мой камень был не в огород конкретно ООП, а в огород научного кода в целом.
В основе есть математическая формула, может надо было ее прочитать? Формулы читать умеете?
Когда формула записана в виде реализации процесса её вычисления и размазана на несколько тысяч строк (я не знаю, как человек так умудрился) — нет, не умею.
Во сколько тысяч раз замедлилось выполнение?
Замедлилось? В 10^{-6} тысяч раз, думаю. То есть, ускорилось порядка на три. Не помню конкретные числа, но помню, что когда закидывал очередной временной ряд на этак десяток тыщ точек, то старый лапшекод выполнялся минуты (я успевал задание-другое решить из туториала по линзочкам), а новый — меньше секунды, и боттлнеком становилось рисование графиков.
Если у вас все вычисления проходят через сериализацию в/из строки, то перф убьётся, будь оно трижды написано на C++.
Я понимаю, что ФП-ники решают задачи уровня детского сада и не для суперкомпьютеров и библиотеки аналогичной opt_einsum на Haskell нет.
Я понимаю, что ООПники решают задачи уровня «собачка гавкает, котик мяукает» и библиотеки аналогичной accelerate (или там unbound, sbv, тысячи их) на ООП нет.
На квантовых компуктерах ФП-шная парадигма может и сработать, потому что там 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 и вайб-кодинга!
Ага-ага! Коллапс волновой функции куда ложится?
Мы это уже обсуждали в соседнем треде про MWI: ложится в коробочку когнитивных подпорок, недалеко от Санта-Клауса, потому что коллапса нет.
Какая система типов отражает квантовые состояния?
Глубоко ответить на этот вопрос я вам смогу через несколько лет, когда зашарю, например, «a first course in topos quantum theory» (а до неё — несколько других, более классических вещей).
Неглубоко — соответствующая внутренней логике некоторой категории, выражающей язык квантмеха.
Но вообще те же линейные + зависимые типы, скажем: вот чуваки eDSL'ку делают поверх идриса — https://arxiv.org/pdf/2111.10867 . Там и про неунитарные преобразования есть (этот ваш пролапс волновой функции).
Опять какая-то императивщина. Ладно, буду 15 раскладывать на множители, с помощью
Microsoft.Quantumи вайб-кодинга!
Добавьте в промпт нейробалаболу «with quipper», скажем, получится функциональщина.
Какое-то фантастическое желание избегать свободные монады и eDSL. Не понимаю.
О, кстати,
Quipper is based on a classical programming language called Haskell, which is particularly suited to programming for physics applications. It provides a high-level circuit description language that includes gate-by-gate descriptions of circuit fragments, as well as powerful operators for assembling and manipulating circuits.
(жирный лол мой)
то получаем жрущий полтора гигабайта рамы у меня сейчас gmail, тормозящее дерьмо на электроне, приложения сбера на гигабайты, и регулярно сломанную форму и страницу комментариев на хабре.
Это все на JS, который с сильным ФП-уклоном.
Очевидно, не помогло =)
Расплодилось великое множество объектно-ориентированных языков: C++, Java, C#, JavaScript
На последнем очень хочется остановиться) можно подумать, что это ошибка, но нет, дальше в списке будет и TypeScript, так что нет, автор явно намеренно записал JavaScript в расплодившиеся объектно-ориентированные языки с начала 90-х.
А теперь следим за руками:
Официальная дата запуска JavaScript - 4 декабря 1995 года.
Классы в JavaScript появились в ECMAScript 2015 (ES6), выпущенном в июне 2015 года.
И даже в таком случае это тоже функции, объявляемые иначе, о чём прямо сказано на 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) Ну и смысл читать дальше после подобных заявлений? Человек же явно не понимает, о чём говорит
Терминология — мать порядка.
Начнем с того, что у термина «объектно-ориентированный язык» есть два практически прямо противоположных друг другу значения: аланокаевское и джеймсоногослинговое.
Вика предлагает определение, списанное под копирку у Лема из «Дневников Ййона Тихого»:
Объектно-ориентированный язык программирования (ОО-язык) — язык, построенный на принципах объектно-ориентированного программирования.
Инстансы тайп-классов в хаскеле очень сложно не назвать объектами по Гослингу.
И вообще, какой язык называть «объектно-ориентированным», а какой «функциональным» в 2026 году, когда в джаве уже не продохнуть от лямбд и ленивых стримов — это теософский спор, как про табы и пробелы.
Уже давно нет (к вопросу про джаву)
набор основных понятий ООП: абстракция данных, инкапсуляция, наследование, полиморфизм подтипов, класс, объект
Давайте я еще раз попробую. «Основных понятий ООП» не существует в аксиоматике нашего мира. Алан Кай под ООП подразумевал одно, Гослинг — совершенно другое. Тут уместно вспомнить еще Матца, доведшего идею «everything is an object» до абсурда в руби.
Потом еще на горизонте появился Боб Мартин, со своими мухоморовыми галлюцинациями. Графомания этого инфоцыгана, в жизни не написавшего ни строки серьёзного кода, внезапно была воздвигнута на хоругви (потому что люди en masse туповаты, и без шор и узды в приниципе ни на что не способны). В результате у нас появилось определение ООП размером не менее трех печатных листов.
Инкапсуляция, полиморфизм и еще добрый десяток «основных понятий ООП» — прекрасно доступны во всех языках, от КОБОЛа до Хаскеля. Остальные — наследование, классы и прочие паттерны — никому нафиг не нужны (единственное разумное «наследование» вместо правильного решения — полиморфизма — реализовано в джаваскрипте).
То ООП, которое придумал Кай ко всему этому никакого отношения не имеет. То, которое предлагал Матц — тем более. Гослинг ну кхм.
В этом смысле, если выбросить в мусорную корзину бобмартиновщину и фаулерщину, — то инстансы тайп-классов в хаскеле — это хорошие, правильные объекты.
Сеньор Матюшкин, вы снова с нами?
википедийное определение, которое более-менее совпадает с общепринятым
«Обще-» это кем конкретно?
ООП-шной инкапсуляции в хаскеле нет […]
Я прочитал «Введение в Хаскель за 7 часов» и знаю на базовом уровне, как там устроена инкапсуляция. А вот что такое «ООП-шная инкапсуляция», простите уж, я не в курсе. Это какая-то особенная инкапсуляция? С блекджеком и методистками?
модули считать объектами нельзя, поскольку в хаскеле они, в отличие от окамла, не first-class
Ого! Окамл-то тут при чем? В Окамле что же, настоящее православное общепринятое ООП?
Я спорю с тем, что хаскель это объектно-ориентированный ЯП.
Очень за вас рад. А кто именно такой тезис высказал? Может быть, нам этого человека тоже позвать?
объекты круга и квадрата с общим методом area
Полиморфизм, реализованный через наследование, — это вообще никаким боком не часть парадигмы, это детали имплементации. Вы просите, чтобы вам повторили слово в слово джавовый код на хаскеле, а лучше бы попытались понять, о чем вообще талдычит собеседник.
А кто именно реализовывать через наследование просил?
Мне неизвестны иные способы «привести пару примеров […] объекты круга и квадрата с общим методом area».
Если имелось в виду не с «общим методом», а «каждый с методом area» — уверен, вы сами осилите написать такой пример на хаскеле.
третий комментарий пытаюсь
Второй, но я уже заметил, что вам до фени факты,
ему, видимо, приятнее пофилософствовать про ООП, дядю Боба и т.д., вместо того, чтобы реально привести код
Естественно. Это же хабр, а не профессиональный форум. Убеждать лично вас мне без надобности, писать тривиальный код, который бесплатный дипсик за десять секунд может нагенерировать — тем более.
Я развлекаюсь, глядя, как вы пыжитесь. Для этого и существует хабр, разве нет?
Почему-то автор рассматривает только написание кода, но не весь его жизненный цикл. ООП при всех его недостатках упрощает и удешевляет развитие программного продукта. А в Rust даже простое добавление нового поля в структуру может потребовать либо дублирования кода, либо массового переписывания трейтов.
Не понял, если просто добавить поле в структуру, то какое дублирование нужно? В метод конструктор в Self{} дописать поле?)
И какие вы трейты собрались переписывать, когда в трейтах нельзя получить доступ к полю структуры?) Если вы имели ввиде переписывать имплементации, то это ничем не больше чем переписывать теже имплантации в любом ООП языке.
И какие вы трейты собрались переписывать, когда в трейтах нельзя получить доступ к полю структуры
Типаж это не только его описание, но так же и его имплементация. Они неотделимы.
это ничем не больше чем переписывать теже имплантации в любом ООП языке
За тем исключением, что в ООП можно унаследовать классы, а не переписывать как их, так и код, их использующий. Если в каких то методах класса иногда может потребоваться новый параметр, связанный с новым свойством (полем структуры в Rust), то достаточно перегрузить только эти методы.
Особенно ярко это проблема на Rust проявляется при динамическом связывании.
ООП при всех его недостатках упрощает и удешевляет развитие программного продукта.
Проблема ромба, исключения в рантайме и хрупкое наследование передают привет) я обо всём этом уже писал в статье.
Хочется дополнить, что если смотреть на языки с точки зрения дилетанта — есть 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 метода вместо одного мембера удобнее?
Класс юзается в 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++ там немеряно.
Вы провидец и точно знаете, какой класс может быть использован в будущем на ESP32 или каком-то ином МК со своими ограничениями, а какой нет?
Я — с достаточной точностью знаю. Например, класс для описания соединения с биржей со специальными для HFT финтифлюшками вряд ли будет использован на ESP32.
Более того, весь этот тред очень ироничный: «использование на $device» отождествляется с «отладкой бизнес-логики на $device», но превратить это в шутку о качестве кода и количестве багов в нём у среднего плюсиста читателю предлагается в качестве самостоятельного упражнения.
Я — с достаточной точностью знаю. Например, класс для описания соединения с биржей со специальными для HFT финтифлюшками вряд ли будет использован на ESP32.
Обоснуйте. Я не вижу почему этот класс не может быть использован, например, на STM32F7 с гигабайтом PSRAM. Да и вообще на любом CPU без аппаратной поддержки виртуальной памяти.
Потому что его смысл — участвовать в торгах на бирже с максимальной скоростью. Никто не торгует высокочастотно на бирже с STM32F7. С FPGA — торгуют, с обычных x86_64 — торгуют. С армов когда-нибудь торговать могут, если уже не. С таких железок — нет, потому что в чём смысл?
Так наоборот, на специализированном МК вполне можно достичь куда большей скорости, чем на CPU общего назначения. В данном случае наличие виртуальной памяти может только увеличить латентность, но уж никак не уменьшить.
А нет менеджера виртуальной памяти - нет и возможности неограниченного использования watchpoints, так как остаётся только весьма ограниченное количество аппаратных watchpoints. ЧТД.
Вы вообще меня удивляете. Неужели впервые слышите о том, что для систем реального времени, где низкая латентность критически важна, специализированные MK и SoC куда более предпочтительны, чем CPU общего назначения?
Именно поэтому в подобных местах повсеместно предпочитают MK и SoC (часто в виде ПЛК), вместо обычных компьютеров.
С FPGA — торгуют, с обычных x86_64 — торгуют
Ну так специализированные MK и SoC и есть золотая середина между FPGA и CPU общего назначения. Причём нисколько не исключают наличие FPGA в своём составе.
для систем реального времени, где низкая латентность критически важна, специализированные MK и SoC куда более предпочтительны, чем CPU общего назначения?
Именно поэтому в подобных местах повсеместно предпочитают MK и SoC (часто в виде ПЛК), вместо обычных компьютеров.
Бред
Чистое дилетантство
Расскажите это технологам на производстве, где даже микросекунды играют роль )))
Впрочем, дискутировать с демагогом, вынужденным переходить на личности и оскорбления в технической дискуссии, смысла нет.
Я разработчик асутп много много лет.
Мне дилетантские заявления непричастных к отрасли людей режут слух
Мне это не нужно. Я знаю технические требования к системам управления большинства пром концернов.
И могу на них ссылаться. При желании
Дело в том, что в реальном мире наносекунды процессора не имеют смысла по ряду причин.
А какие процессоры стоят в Плк, можете поискать. Обычные там.
Ну и вообще, ттх такого оборудования в общем доступе
Дело в том, что в реальном мире наносекунды процессора не имеют смысла по ряду причин.
При чем тут наносекунды процессора, если в данном случае речь о латентности FPGA Cyclone под управлением процессора?
А какие процессоры стоят в Плк, можете поискать
Ага. Например, Siemens S7-1500 с TM FAST Modules, которые явно на FPGA.
Я смотрел спеку на CPU-ядра. Почему на них смотреть не надо?
Потому что производительность подобных SoC определяется в первую очередь FPGA, а не CPU.
Так наоборот, на специализированном МК вполне можно достичь куда большей скорости, чем на CPU общего назначения. В данном случае наличие виртуальной памяти может только увеличить латентность, но уж никак не уменьшить.
При прочих равных.
А прочие равные — это и скорость самой железки (216 мегагерц судя по беглому гуглу? очень смешная шутка), и наличие дров для сетевых карточек, которые делают много интересных вещей на своём уровне (привет solarflare), и так далее.
Неужели впервые слышите о том, что для систем реального времени, где низкая латентность критически важна, специализированные MK и SoC куда более предпочтительны, чем CPU общего назначения?
Да, впервые. Особенно учитывая разницу на порядок в одной лишь частоте, и на два порядка — если учесть архитектурные особенности (под x86_64 я могу написать хешмапу, которая за счёт avx512 проверяет 16-32 элемента за раз, под это недоразумение для данной задачи — нет).
Именно поэтому в подобных местах повсеместно предпочитают MK и SoC (часто в виде ПЛК), вместо обычных компьютеров.
Не видел ни одного HFT-шопа, где использовали бы подобные МК, а я общался с кучей разных, от всяких стартапов «под радаром» до чуваков из гордых золотых спонсоров cppcon (три из четырёх, лол).
А HFT — это предельно конкурентная среда, и если бы можно было обыграть людей, выкинув x86 и поставив пердюшку за 10 баксов, люди бы это давно уже сделали. Но они этого почему-то не делают.
Ну так специализированные MK и SoC и есть золотая середина между FPGA и CPU общего назначения.
Только если в функции оптимизации знаки перепутать.
В этих МК невозможно запрограммировать алгоритм на уровне железа/вентилей, и невозможно быстро считать более сложные модели, чтобы управлять FPGA'шкой с простой моделью (стандартный финт: на FPGA делается линеаризованная модель с околонаносекундным временем отклика, константы которой подсчитываются и обновляются на большой x86-машине, стоящей рядом, требования по латентности к которой меньше, но более быстро считаемая более сложная модель всё равно означает больше денег).
пердюшку за 10 баксов
Вы ошиблись на три порядка. Такие SoC стоят десятки тысяч долларов.
Не понял, какие десятки тысяч?
Первая ссылка в гугле по «STM32F7 price» — https://www.digikey.com/en/product-highlight/s/stmicroelectronics/stm32-f7 , 7-18 долларов.
То, что выделено синеньким - это ссылка. Откройте и посмотрите на цену.
Я не вижу почему этот класс не может быть использован, например, на STM32F7 с гигабайтом PSRAM.
Altera 100G Development Kit, Stratix V GX Edition
А почему вы дали ссылку на Altera 100G, а не на, например, Porsche 911? Последнее ещё дороже и к вашему изначальному тезису имеет примерно такое же отношение.
А почему вы дали ссылку на Altera 100G, а не на, например, Porsche 911?
Чтобы Вы увидели цены на топовые SoC.
вашему изначальному тезису
Потому что Вы не умеете читать больше одного предложения )))
"на любом CPU без аппаратной поддержки виртуальной памяти"
"надо обязательно всегда" - никто кроме тебя этого не утверждал. Это защита от дурака. Не помогла.
По адресу динамически создаваемого объекта? А откуда заранее знать где ему система выделит память?
в раст не завезли ооп, потому что его не завезли в го
Этому тезису никак не мешает то, что раст начали пилить в 2006 (ради Servo, но кто теперь вспомнит), а Go — в 2007?
в целом жить без ооп можно, но иногда неприятно
В целом, с ООП жить можно, — но всегда неприятно.
Да и вообще, в раст не завезли ооп, потому что его не завезли в го.
ООП в Rust было, но задолго до 1.0. У него были классы: источник. В целом, Rust 1.0 отличается очень сильно от Rust Beta, там поломали почти всё.
Плюс ещё процитирую человека ниже:
Этому тезису никак не мешает то, что раст начали пилить в 2006 (ради Servo, но кто теперь вспомнит), а Go — в 2007?
Делается приватное поле с геттерами и сеттеоами вместо публичного, чтобы в будущем при появлении логики при гете/сете api (и abi!) не поменялось.
Все такие статьи структурно однобоки. Берут проблемные места других языков и показывают как их сделать хорошо. Это хорошо. Но не может быть, что в других языках нет хороших мест, которые альтернативой реализуются проблемно. А вот об этом ни слова. Либо читать другую однобокую статью, "хейт".
Возьмем классическое "беспроблемное" наследование много-от-одного. Структура на 5 полей, реализация на 15 методов. В одном наследнике переопределяется метод 7, в другом 7 и 9, в третьем 8, 9 и добавляется 16 "служебный". Какой-то из них может добавить поле в состояние. Другой может стать предком для еще одного наследника. Переопределение какого-то метода, лишь добавление всего одной операции до или после метода предка.
Как? Трейты? Еще трейты? Добавление нового класса в проект потребует переписать существующий код? А без переписывания? Я не знаю. Пока присматриваюсь, хоть уже и долго. Но нигде не встречал удобного решения вполне рядовых задач.



Функциональный Rust. Глава 0: Зачем нужно ФП?