All streams
Search
Write a publication
Pull to refresh
-1
0
Антон Нехаев @nehaev

Архитектор, консультант

Send message
Самое забавное, что даже то, насколько GC влияет на конечное время исполнение программы, тоже напрямую зависит от кривизны рук разработчиков.
А есть такой класс задач «работы с неизменяемым состоянием»? Интересно было бы узнать что туда относится?


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

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

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


Это с философской точки зрения все меняется, и дао подобен реке. А на практике, если внимательно посмотреть на каждую функцию (мы говорим про функциональный подход), окажется, что никакого мазохизма даже близко, в отличие от императивного кода. Входные параметры функции — не меняются в процессе ее работы (во многих императивных языках это тоже стандартная практика). Результат функции получается в процессе обработки входных параметров, но он получается один раз, и не меняется после этого. И даже если результат это какая-то структура, он конструируется весь сразу, одним ударом. Если структура сложная, могут быть промежуточные результаты, из которых уже конструируется конечный, но эти промежуточные результаты также получаются один раз и не меняются после этого.

Есть в императивном программировании такие структуры, с которыми реально удобно работать, именно постоянно их изменяя (коллекции, билдеры). В ФП есть их неизменяемые аналоги, которые при изменении не клонируют все с нуля, а переиспользуют общие данные из предыдущего экземпляра.
1. если требуется именно быстрота, то пишут на C, или смеси C и C++


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


Потому что в «чистых» императивных языках тоже полно всяких проблем. И если решето Эратосфена я писал один раз в жизни как тестовую задачу, а weak hash table использовался всего в двух проектах, из тех, где я участвовал, то обработку коллекций (фильтрация, маппинг, свертка) я пишу по много раз в день, и в императивном виде это просто адски неудобно. Надежный параллелизм реализовать сложно, моки для юнит-тестов иногда выглядят страшнее, чем код, который они тестируют. Люди переходят на ФП не от хорошей жизни, а спасаясь от ошибок, связанных с изменяемым состоянием и побочными эффектами.
И если ваша однопоточная программа на С работает в 1000 раз быстрее, чем однопоточная функциональная программа


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


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

1. Код в функциональном стиле медленне, чем императивный
2. Простые задачи (типа квиксорт) в ФП решаются неадекватно сложным способом
3. Функциональный код пишут только высокомерные математики

В реальной жизни, как всегда, это все несовсем так:

1. Быстродействие программ зависит в первую очередь от архитектуры, дизайна и грамотного применения библиотечных средств (особенно — коллекций), и в последнюю очередь — от выбранной парадигмы
2. Вряд ли такое будет часто, но если вдруг видите, что функционально решение получается слишком сложным — просто перепишите этот кусок в императивном стиле, или тут прям одни Haskell-программисты сидят?
3. Люди бывают разные, делать по отдельныи индивидам глубокие выводы о всем ФП-сообществе неправильно
Мне вот всегда казалось, что фп — это такая до смешного пафосная альтернатива для любителей процедурного программирования, не осиливших то же промышелнное ооп. Наверное, это очень сильно сказано, но таково мое мнение.


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

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

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

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

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

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

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

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

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

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

Корректность лучше быстроты. Простота лучше сложности. Ясность лучше хитроумия. Безопасность лучше ненадежности
— Г. Саттер, А. Александреску. Стандарты программирования на С++

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


Низкая производительность — понятно. Но причем тогда параллелизм? До сих пор не понимаю смысл этого пункта. Особенно: «остерегайтесь людей, которые говорят о масштабируемости программ без учета абсолютной производительности» — что такое «абсолютная производительность», о чем здесь вообще речь?
Статья, которая называется «Недостатки чистого функционального программирования» в основном сводится к тому, что существуют некоторые задачи, которые не удобно решать в рамках ограничений, накладываемых функционально парадигмой. Это примерно как говорить, что недостатком отвертки является то, что ею неудобно забивать гвозди. Если ты взялся забивать гвозди отверткой и тебе неудобно — это твои проблемы, а не недостатки отвертки. Недостаток отвертки — это когда ею неудобно делать то, для чего она предназначена, т.е. закручивать шурупы. Функциональные языки предназначены для работы с неизменяемым состоянием, и пункты 1-5 являются закономерным следствим из этого.

Бросается в глаза желтизна некоторых пунктов:

3. Не существует чисто функциональных потокобезопасных коллекций


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

6. Как оказывается, все существующие реализации функциональных языков спроектированы так, что аллоцируют слишком много памяти


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

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


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

Последние 2 пункта больше ad hominem, чем объективные недостатки ФП как подхода.
Недавно 4 часа сидел в очереди на получение бумажки в одной государственной организации, причем чтобы подать туда документы пришлось еще 2 недели ждать своей «электронной очереди». Такое чувство, что люди прямо по этой инструкции работают.
Это удивительно! Все кого я знаю на такой конфигурации (включая меня) испытывают проблемы. Либо вы созваниваетесь исключительно с линуксоидами и не обновляющими скайп людьми, либо я даже не знаю что думать…
Еще раз, если начинать самому — нормально, если начинает кто-то другой с обновленного Win- или Mac-клиента — трубку снять не получится.

Если у вас нет таких проблем — напишите, пожалуйста, свой дистрибутив и версию скайпа, будет очень интересно заценить.
Не подцепляется на группой звонок, если его начали с обновленного Win- или Mac-клиента. Скриншаринг не работает. Глюки с непрочитанными сообщениями. А так-то да пишет, звонит…

Information

Rating
Does not participate
Location
Россия
Date of birth
Registered
Activity

Specialization

Backend Developer, Software Architect
Lead
Java
Scala