Тем не менее, факт остается фактом: этих аргументов не хватило для того, чтоб функциональное программирование получило широкое распространение. Следовательно, мы должны заключить, что главный недостаток функционального программирования—это обратная сторона его главного достоинства, а именно, что проблемы возникают, когда (как это часто бывает) проектируемая система должна поддерживать какого-то рода состояние.
А я думаю, что причина недостаточной популярности намного проще: программирование в функциональном стиле часто происходит «задом наперед» и выглядит больше как решение головоломок, чем объяснение задачи компьютеру. Часто, когда я пишу на функциональном языке, я знаю, что хочу сказать, но в итоге я решаю головоломки, чтоб выразить это средствами языка. Короче, функциональное программирование просто слишком неестественное.
Чтоб дальше обсуждать функциональное программирование, давайте попробуем испечь пирог. Возьмем рецепт отсюда. Примерно так мы будем печь императивный пирог:
- Разогрейте духовку до 175°C. Смажьте маслом и посыпьте мукой противень. В маленькой миске смешайте муку, пищевую соду и соль.
- В большой миске взбивайте масло, сахар-песок и коричневый сахар до тех пор, пока масса не станет легкой и воздушной. Вбейте яйца, одно за раз. Добавьте бананы и разотрите до однородной консистенции. Поочередно добавляйте в получившуюся кремовую массу основу для теста из п. 1 и кефир. Добавьте измельченные грецкие орехи. Выложите тесто в подготовленный противень.
- Запекайте в разогретой духовке 30 минут. Выньте противень из духовки, поставьте на полотенце, чтоб пирог остыл.
Я позволил себе несколько вольностей с нумерацией (очевидно, каждый шаг—это на самом деле несколько шагов), но давайте лучше посмотрим, как мы будем печь функциональный пирог:
- Пирог—это горячий пирог, остывший на полотенце, где горячий пирог—это подготовленный пирог, выпекавшийся в разогретой духовке 30 минут.
- Разогретая духовка—это духовка, разогретая до 175°C.
- Подготовленный пирог—это тесто, выложенное в подготовленный противень, где тесто—это кремовая масса, в которую добавили измельченные грецкие орехи. Где кремовая масса—это масло, сахар-песок и коричневый сахар, взбитые в большой миске до тех пор, пока они не стали легкими и воздушными, где…
А, ну его к черту—я не могу это закончить! (прим. перев. на самом деле, если следовать логике, даже приведенные пункты должен быть еще сложнее). Я не знаю, как перенести эти шаги в функциональный стиль без использования изменяемого состояния. Либо теряется последовательность шагов, либо надо писать «добавьте бананы», но тогда изменяется текущее состояние. Может, кто-нибудь в комментариях закончит? Хотелось бы посмотреть на версии с использованием монад и без использования монад.
Без использования pipe forward operator:
cake = cooled(removed_from_oven(added_to(30min, poured(greased(floured(pan)), stirred(chopped(walnuts),
alternating_mixed(buttermilk, whisked(flour, baking soda, salt),
mixed(bananas, beat_mixed(eggs, creamed_until(fluffy, butter, white sugar, brown sugar)))),
preheated(175C, oven))))))
C использованием pipe forward operator:
cake = bake(cake_mixture, 30min, prepare(pan, (grease, flour)), preheated(175C, oven))
where cake_mixture =
creamed :until_fluffy ‘butter’ ‘white’ ‘sugar’ ‘brown sugar’
|> beat_mixed_with ‘eggs’
|> mixed_with ‘bananas’
|> mixed_with :alternating ‘buttermilk’ ‘dry_goods’
|> mixed_with chopped ‘walnuts’
where dry_goods = whisked ‘flour’ ‘baking soda’ ‘salt’
У императивных языков-таки есть огромное преимущество в том, что у них есть неявное состояние. И люди, и машины очень хорошо работают с неявным состоянием, привязанным ко времени. Когда вы читаете рецепт пирога, вы знаете, что после выполнения первой инструкции духовка разогрета, противень смазан и мы замесили основу для теста. Это не нужно явно описывать. У нас есть инструкции и мы знаем, что конечное состояние получится путем выполнения этих инструкций. Императивный рецепт никого не поставит в тупик. А если бы мне удалось закончить функциональный рецепт и если бы я показал его моей маме, она была бы им, наверное, очень сильно озадачена. (Ну как минимум версией без монад. Может быть, версия с монадами не так сильно сбивала бы с толку.)
Я пишу этот пост, потому что столкнулся недавно с похожей проблемой. Так уж оказалось, что шаблоны С++ — это функциональный язык. И когда разработчики С++ это поняли, то вместо того, чтоб решить проблему, стали всячески лелеять шаблоны в функциональном стиле, что иногда делает переписывание обычного кода через шаблоны очень утомительным. Например, вот что я недавно написал для парсера. (Я знаю, глупо писать свой собственный парсер, но старые тулзы типа yacc и bison плохие, а когда я попробовал пользоваться boost spirit, то столкнулся с проблемами, решение которых занимало слишком много времени, и в конце концов я просто решил написать свой парсер.)
ParseResult<V> VParser::parse_impl(ParseState state)
{
ParseResult<A> a = a_parser.parse(state);
if (ParseSuccess<A> * success = a.get_success())
return ParseSuccess<V>{{std::move(success->value)}, success->new_state};
ParseResult<B> b = b_parser.parse(state);
if (ParseSuccess<B> * success = b.get_success())
return ParseSuccess<V>{{std::move(success->value)}, success->new_state};
ParseResult<C> c = c_parser.parse(state);
if (ParseSuccess<C> * success = c.get_success())
return ParseSuccess<V>{{std::move(success->value)}, success->new_state};
ParseResult<D> d = d_parser.parse(state);
if (ParseSuccess<D> * success = d.get_success())
return ParseSuccess<V>{{std::move(success->value)}, success->new_state};
return select_parse_error(*a.get_error(), *b.get_error(), *c.get_error(), *d.get_error());
}
Эта функция парсит входной параметр в variant type типа V, пытаясь запарсить входной параметр как тип A, B, C или D.
template<typename Variant, typename... Types>
ParseResult<Variant> parse_variant(ParseState state, Parser<Types> &... parsers)
{
boost::optional<ParseError> error;
template<typename T>
for (Parser<T> & parser : parsers)
{
ParseResult<T> result = parser.parse(state);
if (ParseSuccess<T> * success = result.get_success())
return ParseSuccess<Variant>{{std::move(success->value)}, success->new_state};
else
error = select_parse_error(error, *result.get_error());
}
return *error;
}
ParseResult<V> VParser::parse_impl(ParseState state)
{
return parse_variant<V>(state, a_parser, b_parser, c_parser, d_parser);
}
Этот код чуть-чуть неоптимален, потому что надо выбирать нужное сообщение об ошибке, но в целом это довольно тривиальная трансформация исходного примера. За исключением того, что вы не можете написать так на С++. Как только в игру вступают шаблоны, вам нужно думать более функционально. Вот мой вариант:
template<typename Variant, typename First>
ParseResult<Variant> parse_variant(ParseState state, Parser<First> & first_parser)
{
ParseResult<First> result = first_parser.parse(state);
if (ParseSuccess<First> * success = result.get_success())
return ParseSuccess<Variant>{{std::move(success->value)}, success->new_state};
else
return *result.get_error();
}
template<typename Variant, typename First, typename... More>
ParseResult<Variant> parse_variant(ParseState state, Parser<First> & first_parser,
Parser<More> &... more_parsers)
{
ParseResult<First> result = first_parser.parse(state);
if (ParseSuccess<First> * success = result.get_success())
return ParseSuccess<Variant>{{std::move(success->value)}, success->new_state};
else
{
ParseResult<Variant> more_result = parse_variant<Variant>(state, more_parsers...);
if (ParseSuccess<Variant> * more_success = more_result.get_success())
return std::move(*more_success);
else
return select_parse_error(*result.get_error(), *more_result.get_error());
}
}
ParseResult<V> VParser::parse_impl(ParseState state)
{
return parse_variant<V>(state, a_parser, b_parser, c_parser, d_parser);
}
И я, честно говоря, очень доволен этим вариантом. Конечно, это тяжелее читать, потому что итерирование скрыто теперь в рекурсии, но если б вы только видели мой код до того, как я придумал это решение… У меня была структура с полем std::tuple<std::reference_wrapper<Parser>…>. Если вы когда-нибудь работали с кортежем переменной длины из стандартной библиотеки (то бишь variadic sized std::tuple), вы должны знать, что одно это превращает любой код в ребус.
Так или иначе, мой посыл таков: у меня был простой императивный код, который делал одно и то же несколько раз. Чтоб переписать его через шаблоны, нельзя просто так взять и обернуть повторяющийся фрагмент в цикл. Вместо этого надо полностью поменять логику программы. И для этого нужно решить слишком много головоломок. Более того, я даже и не решил их с первого раза. В первой попытке я остановился на чем-то слишком сложном и так и оставил императивный вариант. И только после возвращения к проблеме через несколько дней я придумал более простое решение выше. Переписывание кода через шаблоны не должно быть таким сложным. Когда основная проблема не в том, чтоб понять, что должна делать программа, а в том, чтоб понять, как заставить ее это делать.
И у меня такое ощущение в функциональных языках слишком часто. Я знаю, шаблоны С++ — плохой функциональный язык, но даже в хороших функциональных языках я трачу слишком много времени на попытки понять, как именно мне выразить вещи в языке, вместо размышлений о том, что же я хочу выразить.
С учетом всего вышесказанного, считаю ли я, что функциональные языки плохие? Вовсе нет. Польза от функциональных языков совершенно осязаемая. Каждый должен выучить хотя бы один функциональный язык и пробовать применять полученные знания в других языках. Но если функциональные языки хотят стать популярными, они должны быть меньше связаны с разгадыванием ребусов.
Комментарии (319)
Aetet
16.06.2016 08:59+31Да блин, хватит упарываться уже. К чему эта сугубая религиозность: ФП, ООП, это все мелочи. ФП и ООП это всего лишь инструменты для построения абстракции. Мы ж не спорим чо круче пила или молоток. ФП — небольшие функции для обработки состояния без сохранения состояния. ООП — хранение состояния системы во время всего жизненного цикла. Зачем противопоставлять эти концепции? Они ж взаимодополняют друг друга?
ФП всего лишь говорит вам — не хотите неопределенного поведения — уберите сайд-эффекты. Это не значит, что их не должно быть, просто они должны жить отдельно. Отделяйте операции над данными от самих данных. Я для себя выработал следующий подход: Обработка данных — класс, который через конструктор принимает объект с состоянием.
class NumericOperation { NumberState state; NumericOperation(this.state); add() { return state.a + state.b; } } class NumberState { int a; int b; }
Таким образом все состояние над которым мы производим операции можно протестировать, потому что мы его задаем только в конструкторе. Легко пользоваться моками. И да, несмотря на то, что NumericOperation хранит ссылку на состояние это не мешает нам писать в ФП стиле. Ведь функция add — чистая.
Для того, чтобы поддерживать систему и дальше. даже если она будет очень сложной — используйте Dependency Injection. Таким образом вы не будете сами инстанцировать классы, вы будете только указывать каким образом это сделать и какие зависимости должны быть прокинуты. Так можно расширять контракт без потери обратной совместимости на уровне входных данных. Мы не должны зависеть от атомарных типов. Мы должны зависеть от абстракций.Aetet
16.06.2016 09:04+1Таким образом мы можем добавить еще несколько состояний при этом даже не нарушив внешний контракт при условии, что мы возвращаем корректно значение:
class NumericOperation { NumberState state; StringState stringState; NumericOperation(this.state, this.stringState); add() { return state.a + state.b + parseToInt(stringState.c); } parseToInt(String c) { // логика парсинга return someNumber; } }
Ведь за проброску данных отвечает DI и нам достаточно поправить вызов DI, а не копаться в тонне копипасты в коде, где используется new NumericOperation.
ApeCoder
16.06.2016 09:31> Таким образом все состояние над которым мы производим операции можно протестировать, потому что мы его задаем только в конструкторе
Применительно к ООП мне эта концепция кажется странноватой — разве там не основной принцип, что состояние должно быть вообще абстрагированно и мы не можем получить к нему прямой доступ?Aetet
16.06.2016 09:41Собственно у нас так и есть. Все состояние лежит в NumberState.
Пусть читает данные кто хочет. Лишь бы не писали.
Однако немного модифицируем пример, чтобы обеспечить более строгую изоляцию:
class NumberState { int get a; int get b = 10; setA(int a) { this.a = a; } }
В итоге мы получили объект, который мы можем модифицировать, только если явно вызовем ту или иную функцию. Если и такой вариант не устраивает, то наворачиваем еще абстракции с иммутабельными стейтами, Stream, который доносит актуальный стейт в конструктор через подписку и т.д., и т.п.
Важное правило — читает любой, пишет только через внешние методы. О том, как лучше уже реализовывать эти внешние методы, это уже зависит от каждой конкретной команды. Кто-то на конвенциях может договориться. А кому-то нужна большая абстракция, чтобы максимально вывернуть руки.ApeCoder
16.06.2016 10:01+1Вопрос, а зачем их разделять — у нас и так есть совокупность полей и совокупность поведения? Можно посмотреть какой-нибудь менее абстрактный пример
VolCh
16.06.2016 10:25+1Разделить данные и изменяющие их функции (не изменяющие функции в общем случае лучше оставить с данными) — наиболее простой способ обеспечить отсутствие неявных изменений данных. Грубо, если мы вызываем какие-то методы объекта класса SomeObject, то можем быть уверенны, что они не изменяют его состояния, а если вызываем методы объекта класса SomeObjectManager, то уверены, что изменяют (или создают/удаляют).
optimizer
20.06.2016 15:04а как же инкапсуляция, ведь когда-то девизом ООП было «данные+функции».
develop7
20.06.2016 21:36не работает именно поэтому:
I think the lack of reusability comes in object-oriented languages, not functional languages. Because the problem with object-oriented languages is they’ve got all this implicit environment that they carry around with them. You wanted a banana but what you got was a gorilla holding the banana and the entire jungle.
© Joe Armstrong, «Coders at Work»
If you have referentially transparent code, if you have pure functions — all the data comes in its input arguments and everything goes out and leave no state behind — it’s incredibly reusable.
VolCh
21.06.2016 05:57«Наиболее простой» не значит «единственный». Это вопрос соглашений в команде, например вопрос именования. В целом нужно избегать создание методов, которые и возвращают данные, и изменяют их (исключение — различные методики кеширования). Грубо — разбивать их на геттеры и сеттеры, функции и процедуры, запросы и команды.
VolCh
16.06.2016 10:14+1Применительно к ООП мне эта концепция кажется странноватой — разве там не основной принцип, что состояние должно быть вообще абстрагированно и мы не можем получить к нему прямой доступ?
Это извращенная трактовка принципа инкапсуляции, отождествляющая её с сокрытием. Инкапсуляция — это не сокрытие данных от внешнего мира, а объединение логически связанных данных и функций в единое целое.ApeCoder
16.06.2016 10:24+2С моей точки зрения, это полезное извращение — когда снаружи не видно, что есть данные а что есть алгоритмы для получения каких-то свойств, мы можем менять реализацию гарантированно не затрагивая пользователей
VolCh
16.06.2016 10:42Сейчас вы говорите уже и не о инкапсуляции, и не о сокрытии, а о замене реализации — это совсем другой вопрос. Сокрытие по сути решает лишь одну задачу — отделение публичного интерфейса от внутреннего. Но эту задачу можно, как правило, решить множеством способов. Но в общем случае ни один из них не гарантирует, что пользователи не будут затронуты — очень часто они рассчитывают не только на формальную сигнатуру интерфейса, но и на определенное поведение, например, что после вызова setA(5), getA() будет возвращать 5, а не 0 или 100500.
ApeCoder
16.06.2016 12:01+1Это вопрос в определении контракта.
VolCh
16.06.2016 15:01Сокрытие никак не относится к контракту. Максимум можно считать, что сокрытие один из механизмов обеспечения публичного контракта. Вспомогательный, уменьшающий вероятность случайного нарушения контракта.
ApeCoder
16.06.2016 15:37+1Определение контракта это как раз отделение интерфейса в общем виде от деталей реализации. В частности сокрытие реальной структуры состояния
VolCh
16.06.2016 18:16Сокрытие реальной структуры состояния в общем случае ортогонально контракту. Если мы не описали в контракте какую-то важную переменную, то будь она хоть глобальной, не говоря о публичной, то никто ей не имеет права трогать. Принцип контрактного программирования: всё, что не разрешено (не описано в контракте) — запрещено. Это с одной стороны. А с другой, я могу описать в контракте приватную переменную и рекомендовать её получать/изменять через отражения, прямой доступ к памяти и т.п.
Bas1l
16.06.2016 12:27+15Статья, на мой взгляд—в первую очередь попытка рефлексии на тему того, почему же ФП непопулярно. Потому что по объективным критериям функциональные языки, действительно, совсем не популярны. Хотя, как и написано во вступлении, считается, что у них куча преимуществ. Действительно, и следить за состоянием легко, и параллелить как бы легко, и математически красивые и т.п. Но на них никто не пишет. Взять, к примеру, TIOBE index (конечно, он не совершенен, но хоть какой-то). В первой двадцатке нет ни одного функционального языка. И идея автора показалась мне очень хорошей, и пример с пирогом метким. Да, действительно, судя по всему, функциональные языки просто слишком далеки (i) от моделей реального мира, (ii) от моделей задач, что появляются в головах программистов, и (iii) от того, как работает компьютер. Что изменяемое состояние—это то, что близко реальному миру, человеку, компьютеру.
У таких статей есть непосредственная польза. Они могут помочь честнее взглянуть на ФП. Потому что многие функциональные языки описывают себя как очень хорошие и даже популярные, а на самом деле писать на них в итоге почему-то неудобно (и никто не пишет). Вот, к примеру, очень хорошая и глубокая критика языка и «маркетинговой кампании» Haskell. В этом же блоге есть свежая предметная статья с критикой чистого ФП. Такая критика и понимание проблем могут помочь сэкономить много денег компаниям, потому что они не начнут проекты на Хаскеле, к примеру. Помогут исследователям сконцентрироваться на важных аспектах этих языков и т.п. Помогут лучше учить программирование в университатах, опять же.
Вот наглядный пример. Оказалось, что в одном немецком университете есть специальная программа (типа магистерской) на факультете информатики для людей с не-ИТ образованием, где их учат программированию и информатике. И вот знакомая биолог начала по ней учиться (как раз месяц назад). И что бы вы думали? Их начинают учить с диалекта Lisp (ну, то есть, самый базовый вводный курс—на диалекте лисп). И на мой взгляд, это совершенно лишняя трата времени преподавателей и студентов (и трата налогов).
Так что я с вашим комментарием не согласен. Попытки понять, есть ли что-то неестественное в ФП и почему, имеют большой смысл. Точнее, понять, почему ФП неествественное (ведь оно совсем непопулярно) и предупредить об этом тех, кто сомневается или в неведении.
P.S. Я не спорю с тем, что какие-то общие и частные идеи из ФП, безусловно, полезны. Стараться писать чистые фунции, не изменять лишний раз состояние. Pattern matching, опять же. Но _иногда_ нарушить правила ФП намного удобнее и лучше, чем пытаться не нарушать. Стандартный пример—циклы. Писать их через рекурсию—мука. Как только у вас будет массив (список!) NumberState, с которыми надо что-то сделать в чистом ФП—все, пиши пропало. А если еще и операций будет несколько, тоже в списке…
P.P.S. Наконец, в статье не сравнивается ФП и ООП, а скорее ФП и императивное программирование.napa3um
16.06.2016 12:36-2В реальных прикладных задачах места для функционального моделирования предметной области полно, но не каждый проект дорастает до сложности, вынуждающей нести издержки по «решению головоломок ФП» (пока их не превысят потенциальные преимущества в плане управления сложностью всего проекта). Разработчики хеловорлдов постоянно будут критиковать строителей космических кораблей за их переусложнённые подходы (ввиду непонимания первыми проблем последних).
atc
16.06.2016 15:07+4Не могу согласиться, в большей части «космических кораблей» разработчики стараются придерживаться максимально простых решений. Пример тому — ядро linux (даже с++ не рискуют брать, не говоря уже о фп языках), openJDK, postgres, каждый из вышеперечисленных проектов решает сложнейшие задачи, но написан почему-то во вполне императивной парадигме.
В качестве контраргумента могу предложить перечислить крупные opensource проекты, которые написаны преимущественно с fp подходом.napa3um
16.06.2016 15:12-5«Любая достаточно сложная программа на C или Фортране содержит заново написанную, неспецифицированную, глючную и медленную реализацию половины языка Common Lisp.»
atc
16.06.2016 15:17+6Но все же хотелось бы увидеть примеры «космических кораблей на fp», в подтверждение вашего предыдущего высказывания, так как мне таковые еще не встречались.
napa3um
16.06.2016 15:23-9У вас всё ещё впереди. Это моё мнение, не стОит пытаться выпрашивать формальный «пруф», можете не принимать моё мнение или принимать противоположное моему — ваше право.
napa3um
18.06.2016 14:56+2When I find my code in tons of trouble,
Friends and collegues come to me,
Speaking words of wisdom:
«Write in C».
As the deadline fast approaches,
And bugs are all I can see,
Somewhere, someone whispers:
«Write in C».
Write in C, write in C,
Write in C, write in C,
LISP is dead and buried,
Write in C.
( https://www.youtube.com/watch?v=wJ81MZUlrDo )
VoidEx
16.06.2016 13:12+4Как только у вас будет массив (список!) NumberState, с которыми надо что-то сделать в чистом ФП—все, пиши пропало. А если еще и операций будет несколько, тоже в списке…
А можно развернуть, какие возникнут проблемы? Мне, хорошо знакомому с Haskell, совсем неочевидно
VolCh
16.06.2016 15:04+1Их начинают учить с диалекта Lisp (ну, то есть, самый базовый вводный курс—на диалекте лисп). И на мой взгляд, это совершенно лишняя трата времени преподавателей и студентов (и трата налогов).
Смотря какая цель обучения. Если человек успешно пройдёт такой базовый курс, то с императивными языками точно разберётся.
VolCh
16.06.2016 15:05+1Как только у вас будет массив (список!) NumberState, с которыми надо что-то сделать в чистом ФП—все, пиши пропало.
Что? Отфильтровать, свернуть, отразить?
staticlab
16.06.2016 20:08На седьмой позиции JavaScript. Конечно, он не полностью ФП, но в последние годы на нём стало удобно и модно писать исключительно в функциональном стиле. По сути, полноценный SPA-фронтенд вполне можно написать, например, на React/Redux, пользуясь только функциональными инструментами с точностью до вызова ReactDOM.render(). Отсюда превосходная отлаживаемость и тестируемость. Возражения по поводу циклов тоже непонятны: map, reduce, forEach, filter чем не подходят?
taujavarob
16.06.2016 21:04+1staticlab > По сути, полноценный SPA-фронтенд вполне можно написать, например, на React/Redux, пользуясь только функциональными инструментами…
React — это не ФП
Redux — да, это ФП. — Но ему противостоит ООП в виде Mobx
Redux vs Mobx -> ФП vs ООП — можно уже делать ставки кто из них одержит победу! ;-)
staticlab
17.06.2016 08:49В React из ФП есть в виде stateless components. А у Mobx корневое API, включая декораторы, практически всё функциональное.
taujavarob
17.06.2016 14:05staticlab > А у Mobx корневое API, включая декораторы, практически всё функциональное.
Но при работе с Mobx не надо заботится о том чтобы твои объекты(состояния) были immutables
Redux vs Mobx -> ФП vs ООП — Mobx одержит победу, имхо ;-)staticlab
17.06.2016 14:59На первый взгляд их примеры выглядят как приветы от Нокаута и Ангуляра, притом что писать логику инлайном непосредственно в обработчике давно уже дурной тон. Впрочем, я абсолютно согласен, что требование иммутабельности очень сильно затуманивает логику приложения, а react-addons-update и Immutable мало помогают в читабельности. Так что вполне может быть, что Mobx свою нишу найдёт.
TheShock
24.06.2016 09:15+1В React из ФП есть в виде stateless components
stateless components скорее про ооп. Классический view из MVC, где stateless components — View, statefull components — толстый контроллер, а стор и екшены — тонкая модель.
TheShock
24.06.2016 09:13React/Redux, пользуясь только функциональными инструментами
Как минимум необходимость this.setState полностью разрушает чистоту ;) А вообще на практике при сложной модели такая связка скорее становится процедурной, чем функциональной.taujavarob
24.06.2016 18:06TheShock > Как минимум необходимость this.setState полностью разрушает чистоту
3 Reasons why I stopped using React.setState
https://medium.com/@mweststrate/3-reasons-why-i-stopped-using-react-setstate-ab73fc67a42e#.r4wi5oqu0
VolCh
16.06.2016 09:18+3С другой стороны, в реальном мире мы не можем перевести состояние пирога из «приготовленный» в «готовый» без совершения каких-то операций, а в императивном программировании это (прямое задание состояния) сплошь и рядом, что доставляет другую боль. Объединяет два мира принцип: состояние программы (модуля, объекта) нельзя изменить, можно лишь применить к нему «мутирующую» функцию (часто с дополнительными аргументами) и получить новое состояние. Или применить к состоянию «обычную» функцию (часто без аргументов), чтобы получить как
ApeCoder
16.06.2016 09:43Мне кажется, различие только в том, что в императивном мире есть глобальное общеизвестное состояние в объектно-оритетированном мире у объектов есть identity и state, соответственно, есть понятие изменения как для глобального объекта «мир» так и для любого конкретного объекта (а чтобы объяснить, что состояние неизменно, нужны спецухищрения). Причем состояния объектов являются частью состояния мира.
В чистом функциональном мире наоборот, эти понятия вводятся дополнительно и локально: если хочешь использовать глобальное состояние надо явно использовать объект RealWord, возможно обернутый в IO monad, можно вводить локальные состояния через State monad, причем они могут не зависеть от состояния мира и быть множественными. За это платят явной передачей этих аргументов везде, где надо.VolCh
16.06.2016 10:06+1Различие даже не в том, что состояние глобально или локально, а в том, что в идеальном императивном мире можно и нужно состояние менять (напрямую или через функции/методы), а в идеальном функциональном нельзя, можно только получить новое состояние из старого. Философски рассуждая, в реальном мире состояния тоже не меняются, а переходят из одного в другое. Но на обывательском уровне мы считаем, что состояние меняем. Не запускаем процесс перевода воды в чайнике из состояния «холодный» в состояние «горячий», а меняем состояние с «холодный» на «горячий». Философски у объектов реального мира состояние — ValueObject, может и обладающий идентичностью, но на практике редко используемой, сравнение идёт по значениям, переход объекта из одного состояния в другое осуществляется присвоением ему нового состояния, а обівательски у объектов реального мира состояние — Entity, переход объекта из одного состояния в другое осуществляется изменением свойств его состояния.
ApeCoder
16.06.2016 10:21+2Менять это значит что существует то же самое, но с другим состоянием. Соответственно порождение новой пары (ID, State2) из (ID, State1) семантически эквивалентно «Менять»
VolCh
16.06.2016 10:47Философское и обывательское различие в «менять состояние» состоит в том, делаем ли мы Object.setState(transition(Object.getState()) или Object.getState.transition()
ApeCoder
16.06.2016 12:00+2семантически эквивалентно
allObjects2 = transition(allObjects, objectID)
Т.е. если у нас есть RealWorld и мы его с собой всюду таскаем, то функциональное программирование по свойствам становится эквивалетно имеративному
Shamov
16.06.2016 11:08+6К сожалению, в реальном мире мы именно меняем состояние одной единственной воды в чайнике с «холодного» на «горячее», а вовсе не создаём новую «горячую» воду, уничтожая в случае успешного создания старую «холодную».
ApeCoder
16.06.2016 11:57+1«создаем», «уничтожаем» это императивные словечки. Функционально было бы более декларативно типа:
Вскипевший Чайник ЭТО засвистевший(поставленныйНаПлиту(сНалитойВодой(чайник)))napa3um
16.06.2016 12:15-1результат = дождатьсяСвиста(поставитьНаПлиту(налитьВодуВ(чайник))); // каждая функция возвращает новое состояние
результат = чайник;
результат.налитьВоду();
результат.поставитьНаПлиту();
результат.дождатьсяСвиста(); // каждый метод меняет состояние
Первый вариант удобен для конструирования репрезентаций данных в разных формах без влияния логики этих репрезентаций друг на друга, второй вариант удобен для работы с персистентными хранилищами данных; оба варианта одновременно возникают в информационных системах с выделенными архитектурными слоями M и V (или в компонентах/объектах с разделёнными операциями C и Q в терминах CQSR).
Спор приверженцев ООП с приверженцами ФП о преимуществах своих подходов — это спор о преимуществе двигателя перед коробкой передач.alsii
16.06.2016 14:24результат = дождатьсяСвиста(поставитьНаПлиту(налитьВодуВ(чайник)));
эквивалентно:
результат = чайник;
результат = налитьВодуВ(результат);
результат = поставитьНаПлиту(результат);
результат = дождатьсяСвиста(результат);
у нас все еще нет изменяющегося состояния?
Ну хорошо, давайте еще так:
результат = чайник;
результат = результат.налитьВодуВ();
результат = результат.поставитьНаПлиту();
результат = результат.дождатьсяСвиста();napa3um
16.06.2016 14:29Ваш комментарий похож на возражение по форме, но не очень понятно, в чём суть возражения. В функциональной форме в качестве аргумента каждой последующей применяемой к состоянию функции является копия состояния; в императивной форме операции применяются к одному и тому же экземпляру состояния. Что вам непонятно или кажется некорректным?
alsii
16.06.2016 15:01+1чайник — это состояние? чайник — это экземпляр, пребывающий в состоянии. результат — это некое хранилище, где хранится экземпляр, пребывающий в состоянии.
Я к тому, что вот у нас есть ящик результат, в котором стоит чайник, потом некоторый процесс (ну стого говоря исполнитель этого процесса) налить воду достает его из ящика, проверяет его состояние, порождает новый чайник в состоянии "заполненный водой" и кладет его обратно в ящик. Как это выглядит с точки зрения стороннего наблюдателя? Он заглянул в ящик, и увидел там пустой чайник, потом заглянул еще раз, и увидел чайник заполненный водой. С его точки зрения этот тот же самый чайник, потому что для него это "чайник который находится в ящике результат. И он однозначно изменил состояние. Отсюда: присваивание меняет состояние переменной.
Насколько я понимаю набор правил в ФП не выполняется, а вычисляется. Пирог же нельзя вычислить. Его можно испечь, выполнив определенные действия. Более того, сам процесс вычисления необходимо выполнять, и в этом процессе будет масса объектов и состояний. Хотя… Возможно существует интерпретатор функционального языка, написанный на функциональном языке?napa3um
16.06.2016 15:07-2Чайник в ООП — это объект с изменяемым состоянием и с неявными зависимостями нового состояния от предыдущего, чайник в ФП — это фабрика состояний чайника с явно описываемыми зависимостями нового состояния от предыдущего. Но не пытайтесь продолжением подобных распросов научиться ФП, лучше более методично посвятите этому своё личное время, не отнимая моего.
alsii
16.06.2016 15:38+1Спасибо за совет. Я пробовал. Вопросов стало больше. Не смею больше беспокоить.
Flammar
16.06.2016 16:00Про чайник в ФП как фабрику состояний чайника — интересный взгляд, не встречал раньше…
Sirion
17.06.2016 03:03+2После прочтения этой ветки комментариев я уже совсем иначе воспринимаю эту обложку.
Заголовок спойлера
VolCh
16.06.2016 15:24И он однозначно изменил состояние.
Не однозначно. Вполне может быть, что он принял новое состояние, а не изменил имеющееся.
Насколько я понимаю набор правил в ФП не выполняется, а вычисляется.
В общем случае при моделировании реального мира набор правил последовательно (или параллельно) применяется к состоянию объекта, порождая новые состояния и, возможно, производя побочные действия, типа записи в логах :)
Bronx
22.06.2016 07:20+1> Он заглянул в ящик, и увидел там пустой чайник, потом заглянул еще раз, и увидел чайник заполненный водой.
Разница начинается в тот момент, когда наблюдатель открывает ящик в середине процесса и суёт палец проверить уровень воды. В императивной парадигме у него есть шанс нащупать полунаполненый чайник, или не нащупать его вовсе, а вместо этого тыкнуть пальцем в глаз другому наблюдателю, наполняющему чайник. Чтобы этого избежать, нужно предусмотреть запирание ящика на время изменения состояния, организовать очередь желающих открыть ящик, избежать ситуаций, когда кто-то открыл ящик A и хочет открыть ящик Б, а кто-то другой — наоборот (дедлок).
В функциональной парадигме таких проблем просто нет: новое состояние либо уже существует целиком, либо не существует вовсе, и для этого не нужно устраивать кучу церемоний. Пока чайник не вскипел и пирог не испёкся, их никто наблюдателю никогда не выдаст.
Эрго, императивщина вполне работает в случаях, когда наблюдатель строго один, и его не беспокоит возможность увидеть грязное закулисье мира. В этой парадигме все наблюдатели живут в едином времени (как в ньютоновской физике), и это порождает парадоксы конкурентного доступа. Функциональщина хороша, когда наблюдателей куча, но нужно чтобы у каждого был свой собственный параллельный мир, не конфликтующий с мирами других наблюдателей. Каждый наблюдатель живёт в своём «собственном времени» (как в специальной теории относительности), что разрешает парадоксы, но труднее для обыденного восприятия.
kx13
16.06.2016 14:25+1> Спор приверженцев ООП с приверженцами ФП о преимуществах своих подходов — это спор о преимуществе двигателя перед коробкой передач.
Скорее вопрос применения неправильного типа двигателя. Например установки на тележку для гольфа дизельного двигателя, вместо электрического. И то и другое работает, но одно из решений не оптимально.
Собственно это автор и показал на примере с рецептом. Для рецепта функциональный подход не подходит т.к. это типичная императивная задача. В то же время в статьях про ФП можно найти кучу примеров, где задача через ООП делается сложно и громоздко, а в функциональном стиле просто и удобно.
Вот поэтому и надо изучать разные парадигмы, чтобы понимать, что и где удобнее.napa3um
16.06.2016 15:02-1Вы опять противопоставили ФП и ООП в стиле противопоставления дизельного или бензинового двигаетля в выборе оборудования для автомобиля. Я же как раз и утверждаю, что ФП не заменяет ООП, а живёт с ним рядом, беря на себя другие функции в составе всего автомобиля (проекта).
Flammar
16.06.2016 14:55Для каждого промежуточного результата сделать отдельный тип. Для каждой операции сделать метод в каком-нибудь классе. И заавтовайрить через dependency injection. Пусть контейнер императивные цепочки строит.
FForth
16.06.2016 21:04: ДождатьсяСвиста Чайник НалитьВоду ПоставитьНаПлиту;
P.S. Состояния вызываются последовательно (контекст — Чайник тоже может передаваться между словами)ApeCoder
17.06.2016 11:36Только если како-то слово запихает на стек больше ем должно, или возьмет больше надо запихивать в стек в свой мозг и им париться
Shamov
16.06.2016 12:23+1С этим ничего не поделаешь. Человек мыслит «на языке». И хотя некоторые с этим не согласны, в науке это мейнстримно признанная гипотеза. Она называется гипотеза Сепира-Уорфа.
Bas1l
16.06.2016 12:31А почему тогда все еще называется гипотезой?
napa3um
16.06.2016 12:43Потому что границы применимости этой гипотезы в качестве аксиомы в какой-либо формальной системе очень условны (нет достаточной конкретики в терминах «мысль» и «язык», например).
Shamov
16.06.2016 12:46+1Потому что нельзя залезть в голову к другому человеку и точно узнать, как именно он мыслит. Сами же люди обычно отрицают, что свобода их мысли ограничена языком. Однако все косвенные свидетельства указывают на то, что всё именно так. Причём к языкам программирования это всё тоже относится в полной мере. Более того, некоторые лауреаты премии Тьюринга говорят об этом открыто. В Википедии, в статье про саму гипотезу, есть отдельный раздел, посвящённый языкам программирования.
ApeCoder
16.06.2016 12:36Поинт не в том, на языке или не на языке, а в том, что сама по себе семантика императивная. Мы не создаем ничего и не уничтожаем, мы просто описываем что нам надо. Что нам не надо получается оттуда само и уничтожается или создается при трансляции на нижележащие императивные уровни
Shamov
16.06.2016 12:53Семантика — это свойство языка. У императивных языков императивная семантика. В них есть такие категории как «создать», «уничтожить». У функциональных же языков семантика не императивная. Им такие категории чужды. Вместо этого у них есть другие, которые позволяют лаконично описывать результат (конечный или промежуточный), но плохо подходят для прямого описания последовательных шагов.
ApeCoder
16.06.2016 13:18+1У IO monad императивная семантика или функциональная? Я имею ввиду, что императивный язык можно рассматривать как приложение функционального языка
Shamov
16.06.2016 14:05IO-монада — это костыль, предназначенный для скрещивания функциональной семантики с императивной. Соответственно, и семантика у неё костыльная… т… е. смешанная. Императивный язык можно рассматривать как приложение функционального только при наличии соответствующих костылей. Без соединяющих костылей императивные языки практически бесполезны в мире чистых абстракций, а функциональные — в реальном мире. Между прочим, существует костыль и для соединения в обратном направлении — библиотека шаблонов STL. Она позволяет выражать логику программы почти на чистых абстракциях в сугубо императивном языке.
napa3um
16.06.2016 14:08+1Костылём это выглядит только для тех, кто считает ФП ультимативным отказом от побочных эффектов в функциях, а не способом разделения функций на «чистые» и «влияющие на состояние».
Shamov
16.06.2016 14:29+2Это жульничество. Вы просто пытаетесь «подкрутить» определения понятий таким образом, чтобы критически важная часть инструментария, делающая его практически полезным, не выглядела костылём. Это то же самое, что называть педерастию — вариантом нормальной ориентации, смерть — альтернативным жизненным статусом, ребёнка — будущим взрослым, а падение экономики — отрицательным ростом. Когда речь идёт о ФП, то чистые функции — это его альфа и омега. В рамках ФП нельзя просто так разделить функции на «чистые» и «нечистые». Как только такое разделение происходит, то автоматически происходит выход за рамки ФП в сторону старой-доброй императивности. Конечно, это очень небольшой шаг. Но глупо отрицать, что он есть. ФП только с «чистыми» функциями отличается от ФП с примесью «нечистых» функций примерно так же, как математика отличается от прикладной математики… т.е. это вообще другая дисциплина.
napa3um
16.06.2016 14:35-1Я не пытаюсь подкрутить определение, я пытаюсь подкрутить мотивы собеседника в использовании инструмента ФП в «реальных проектах» (и снизить риск разочарований от его неоправданного использования). Если чертёжнику дать циркуль, он не будет переживать по поводу того, что циркулем нельзя вычертить весь чертёж, но останется доволен тем, что окружности теперь получаются ровные.
Shamov
16.06.2016 15:14+1Всё верно. Но в аналогии с чертежом и циркулем как будто бы нет никаких костылей. В то время как в реальной чертёжной практике они есть. Существует очень много соглашений о том, как лучше чертить разные элементы, как их подписывать, как обозначать размеры. Все они призваны повысить общую наглядность чертежа. Без таких соглашений, стыкующих между собой разные элементы, любой сложный чертёж выглядел бы бесполезным месивом из линий. Эти костыли не воспринимаются как таковые лишь потому, что чертёжников обучают им с самого начала, постепенно приучая их к мысли о том, что все эти условности — неотъемлемая часть чертёжного мастерства. Так же и с монадами. Совершенно чуждая функциональной парадигме вещь вплетена в неё настолько изящно, что не сразу и сообразишь, что это всего лишь прикладной костыль для того, чтобы парадигму можно было применять в «реальных проектах».
0xd34df00d
17.06.2016 04:41+1Давайте лучше начнём с семантики монады ST? Какая у неё семантика?
Как это связано с тем, что ST — escapable, а IO — нет? Как это всё связано с rank-2 polymorphism?
ApeCoder
16.06.2016 15:41+2Это настолько же костыль, насколько библиотека windows forms костыль для C# для работы с окнами. Это просто способ выразить императивную семантику через функциональную. В clean для этого используют объект World
Shamov
16.06.2016 15:59+2Другими словами, это костыль на 100%. Когда некий новый язык позволяет удобно работать с окнами лишь при помощи специально разработанной для него библиотеки, то я такую библиотеку как раз и называю костылём, соединяющим новый язык со старой оконной подсистемой. И если это не костыль, то тогда что же считать костылём?
VoidEx
16.06.2016 16:20+3Любая библиотека — костыль? Или только та, без которой удобно не поработать? А если можно удобно и без библиотеки, зачем она вообще нужна?
Костылями зовут кривые решения, затыкающие какую-то проблему, но не устраняющие причин и не обладающие достаточной общностью.Shamov
16.06.2016 16:38Ну, в контексте данного разговора я называю костылями только, скажем так, парадигмальные костыли. Т.е. такие костыли, которые нужны для работы в одной парадигме на языке, у которого парадигма совсем другая. Такие костыли затыкают проблему несоответствия парадигм друг другу.
ApeCoder
16.06.2016 16:44То есть чем больше язык позволяет выразить при помощи библиотек тем он костыльнее. Наверное самый костыльный это ЛИСП
napa3um
16.06.2016 16:56Парадигма автомобиля заключается в перемещении полезного груза по поверхности. Нужно выкинуть из автомобиля все детали кроме колёс как костыли?
Shamov
16.06.2016 17:27Костыли не нужно выкидывать. Зачем? Без них ничего работать не будет. Нужно просто избегать стокгольмского синдрома. Т.е. избегать мыслей в стиле, что эти прекрасные костыли, отлично выполняющие свою задачу, — это новые ноги. Костыли нужны, чтобы помогать неработающим ногам ходить, как бы, на ногах. Но они не нужны тем, кто отказался от идеи прямохождения и пересел в коляску. Коляска — это другой тип костылей, но сейчас речь не об этом. Главное, что в парадигме прямохождения всё, кроме собственно ног, является костылями.
Source
16.06.2016 13:49плохо подходят для прямого описания последовательных шагов.
Почему же, для описания последовательных шагов в виде цепочки функций ФП отлично подходит. Но хуже подходит для описания нескольких взаимовлияющих на разделяемое состояние последовательностей. Впрочем, до абсурда можно что угодно довести… давайте печь пирог при помощи ООП и классов Духовка, Противень, Миска, Мука, Сода, Соль, Масло, Сахар, Яйцо, Кефир, Банан, Орех, Полотенце.
Автор тупо хитрит, когда приводит рецепт из кулинарной книги в качестве императивной программы. Рецепт — это далеко ещё не программа.Flammar
16.06.2016 13:59+1давайте печь пирог при помощи ООП и классов Духовка, Противень, Миска, Мука, Сода, Соль, Масло, Сахар, Яйцо, Кефир, Банан, Орех, Полотенце.
Ниже приводили пример, когда духовок, противней, мисок и всего остального имеется в наличии по много экземпляров. Тогда такой прикол с классами, состояниями и событиями будет вполне уместен.Source
16.06.2016 17:56+1Это для конкурентного то выпекания? Ну да мьютекс на миску — это так похоже на реальный мир, не то, что сообщение от одного пекаря другому, что миска освободилась :-D
Azoh
17.06.2016 13:06+2Как раз-таки в реальном мире миски обычно не передаются сообщениями, а захватываются в пользование, т.е. аналогия мьютекса ближе.
Shamov
16.06.2016 14:10Слово «последовательные» применительно к шагам алгоритма обозначает строго прямую последовательность… не обратную.
VolCh
16.06.2016 15:27Не путайте последовательность выполнения и последовательность записи.
Shamov
16.06.2016 15:52Я ничего не путаю. Просто ситуацию, когда прямую последовательность выполнения нужно записывать в обратном порядке, я как раз и называю словами «плохо подходит для описания».
ApeCoder
16.06.2016 15:57+2Эта последовательность записи просто для того, чтобы было понятнее неФП людям. ФП люди используют pipe оператор или еще что:
тесто |> месить |> поставитьВДуховку |> влючить
оператор |> берет левую часть и применяет к ней функцию, указанную в правойShamov
16.06.2016 16:11В итоге мы пришли к тому, что функциональные языки больше подходят для описания функциональных алгоритмов. Замечательно! Но только как-то по-капитански…
ApeCoder
16.06.2016 17:19+2Еще раз — в функциональных языках можно писать последовательность вызовов функций в прямом порядке из этого никак не следует то, что вы сказали
Source
16.06.2016 17:48+2И что? Вы считаете, что на функциональных языках нельзя прямую последовательность действий записать?
Вот, например, выпекание пирога на Elixir:
http://pastie.org/10879491
Запускается 4 актора (печь, противень и 2 миски) и всё прекрасно записывается при помощи отправки им сообщений.
Да, эта задача вертится вокруг состояний, но не надо думать, что функциональные языки не могут работать с состояниями. Просто они не используют для этого классы.
k12th
16.06.2016 13:09Признана она в лучшем случае в своем слабом варианте.
napa3um
16.06.2016 13:20-3Отнюдь, вся современная когнитивистика неявно опирается на символизацию модели реальности в голове субъекта. Также феномен феральных детей очень хорошо обосновывается этой гипотезой.
Shamov
16.06.2016 13:41Лишь в силу того, что сами признающие слабы духом. Им трудно признать, что они несвободны даже в своих мыслях.
Idot
17.06.2016 11:50А как быть с теми кто размышляя размахивает руками рисуя образы?
PS а вообще, математики делятся на Алгебраистов и Геометров, которые привыкли размышлять по-разному.Shamov
17.06.2016 12:40+1Язык жестов — это тоже язык. Образами люди мыслят только во сне. Именно поэтому там всё так мутно и иррационально. Если тебе во сне кажется, что ты видишь издалека, например, своего друга Пашу, и ты подходишь, чтобы рассмотреть поближе, то всегда оказывается, что ты угадал… это и правда Паша. Проблема в том, что на языке образов (без заранее оговоренного языка) нельзя выразить даже банальное отрицание. Во сне в мозгу не может возникнуть мысль «это не Паша». Любой, кто мог бы быть Пашей, всегда неизменно оказывается Пашей.
napa3um
16.06.2016 12:52В реальном мире мы как сознательные интеллектуальные агенты описываем то, что мы видим (создаём субъективные репрезентации объективной реальности), и пытаемся оказать влияние на то, что мы видим (влияем на объективную реальность в терминах выбранной репрезентации реальности). Первую часть процессов в нашем мозгу удобнее выражать в парадигме отсутствия изменяемого состояния системы (но наличия множества форм отображения этого состояния), вторую часть удобнее выражать в парадигме явного изменения состояния системы (с автоматическим перестроением всех репрезентаций, построенной первой частью процесса). ФП — не замена ООП, а дополнение, необходимое для более полного описания сложной информационной системы.
Shamov
16.06.2016 13:01-2Фразочки вроде «субъективная репрезентация объективной реальности» давно пора забыть. Они звучали свежо во времена Декарта. В наши же дни философия ушла далеко вперёд. И уже минимум сто лет не считается, что объективная реальность на самом деле существует. Новая парадигма состоит в том, что существует только субъективная реальность, иногда кажущаяся объективной тем, кто недостаточно проницателен.
napa3um
16.06.2016 13:07+3Вы озвучили точку зрения так называемого наивного идеалиста или солипсиста, которая как раз и была актуальна во времена Декарта. А в современном научном (нео/пост)позитивизме существование объективной реальности постулируется как данность, а не как что-то, что нужно обосновывать (хотя вот свойства этой объективной реальности в своём пределе могут быть и непознаваемыми, позитивисту будет достаточно работающей формулы какой-либо физической закономерности и без её философской интерпретации).
Shamov
16.06.2016 13:56Отнюдь. Когда я говорил про сто лет, я имел в виду то, что граница проходит где-то по середине Витгенштейна. Точка перехода — это момент, когда ранний Витгенштейн осознал ошибочность позитивизма, отринул его и превратился в позднего Витгенштейна. Ну, а нео-/постпозитивизм — это очевидная чушь. Особенно ясно это становится после того, как вы обозначили главный момент: существование объективной реальности постулируется как данность. Какой же это вообще позитивизм? Используя такие откровенно читерские приёмчики, можно прийти к абсолютно любым выводам. Схема рассуждений довольно примитивная. Не можем что-то обосновать, но нам очень нужно, чтобы это было так? Не вопрос! Просто постулируем это как данность. А тот вариант, что мы не можем это обосновать именно потому, что это не так, в расчёт не берём.
napa3um
16.06.2016 14:02+2Ваши аргументы поверхностны и наивны, отсылки к Витгенштейну некорректны, а мотивы их озвучивания неясны. Вы пытаетесь меня убедить, что объективная реальность перестала быть предметом научного метода? Не думаю, что у вас получится (проблемы возникнут прежде всего методологические, а не ввиду моего сопротивления вашим интерпретациям). Пытаясь доказать мне «истинное положение вещей в теме интерпретации субъективных переживаний» вы автоматически указываете пальцем на объективную реальность.
alsii
16.06.2016 14:34+2@napa3um Shamov спасибо! Вы сделали мой день. Я всегда подозревал, но сейчас реально увидел, как занимаются философией:
Философ А: нео-/постпозитивизм — это очевидная чушь… Схема рассуждений довольно примитивная… Откровенно читерские приёмчики
Философ Б: Ваши аргументы поверхностны и наивны… Отсылки… некорректны… Мотивы их озвучивания неясны
napa3um
16.06.2016 14:40Это не было занятием философией, это был намёк на невозможность ею заниматься с собеседником в силу его столь ограниченного понимания её методов. Это было вульгарным снобизмом, если вам так приятнее.
Shamov
16.06.2016 14:50+1Непрерывное проявление вульгарного снобизма — это как раз и есть то единственное, что обычный человек может зафиксировать, наблюдая со стороны разговор философов. Не понимая содержания разговора, он видит только его форму и думает, что форма — это всё, что есть. Так что восторженный комментарий вполне корректен и абсолютно точно отражает увиденное.
napa3um
16.06.2016 14:57Для моей собаки разговоры о квантовой механике выглядят шумом, например. Она видит только экспрессивно-фонетическую форму. Потому её восторженный лай не вполне корректен и не является экспертным умозаключением о содержании предмета квантовой механики.
Shamov
16.06.2016 15:22+1То, что собака восторгается именно содержанием, — это ваша ошибочная интерпретация происходящего. Вы считаете, что содержание — это главное, и потому вам кажется, что она восторгается именно им. На самом же деле, собака восторгается экспрессивно-фонетической составляющей, думая, что это всё, что есть в вашем разговоре о квантовой механике. Так что её восторженный лай — это вполне адекватная оценка ваших экспрессивно-фонетических навыков. Возможно, вы и правда в этом очень хороши.
napa3um
16.06.2016 15:39Вы забыли, как написали:
> Не понимая содержания разговора, он видит только его форму и думает, что форма — это всё, что есть.
Теперь попробуйте перечитать мой ответ.
alsii
16.06.2016 15:23+3Вообще говоря, для меня так и осталось загадкой что же такое изучают философы. Курс философии в техническом вузе убедил меня в том, что предмет философии — это изучение мнений абсолютно разных людей по слабосвязному кругу вопросов. Чтобы сдать экзамен требовалось выучить кто и что говорил/писал по определенному вопросу. При этом каких либо доказательств истинности этих мыслей ни в учебнике, ни на лекции не приводилось. Просто факт: А говорил, что Б — это В. Г не соглашался с ним и говорил, что Б — это Д. Е высмеивал их обоих, и говорил, что нам никогда не узнать что такое Б на самом деле, но при этом был точно уверен, что Б это не Ж. Курс назывался "Марксистско-ленинская философия", поэтому в середине главы утверждалось: "Б — это З, потому что так говорили И, Й и, самое главное К. И когда все народы осознают, это, то все заживут дружно и счастливо". Дальше обычно было написано, как замечательно что Б — это З, какие молодцы И, Й, и особенно К. Как мы счастливы, что понимаем это, и как несчастны все остальные. Но этого в билетах уже не было и можно было не читать.
Shamov
16.06.2016 15:38В технических вузах изучают в лучшем случае историю философии, а не саму философию. Чтобы успешно освоить философию нужен гуманитарный склад ума, который позволяет не зацикливаться на том, что существует единственная истина. Философия построена на том, что истин бывает много. Взяв за основу один набор аксиом, можно прийти, например, к тому, что идеальное общество — это коммунистическое общество. А взяв другой набор, можно прийти к тому, что идеальное общество — это либеральная демократия. И никакого противоречия в этом нет. Примерно как с геометрией. Если исходить из того, что параллельные прямые не пересекаются, то можно получить геометрию Евклида. Если же исходить из обратного, то получится геометрия Лобачевского. И никакого мошенничества. И в том, и в другом случае логика всех рассуждений безупречна. Ошибку найти нельзя. Разве что можно посчитать ошибкой решение взять за основу именно эти аксиомы, а не другие.
alsii
16.06.2016 15:53+1Если же исходить из обратного, то получится геометрия Лобачевского.
Или геометрия Римана. И все эти геометрии являются частными случаями, зависящими от изменения одного параметра: кривизны поверхности. И можно построить более общую геометрию, в которая в формулы будет входить кривизна. Как с этим обстоят дела в философии?Shamov
16.06.2016 16:04Философия — это как раз и есть та самая общая геометрия, которую можно настраивать параметрами. Только в качестве инструмента получения нового знания в ней применяется не математика, а логика.
Unrul
17.06.2016 01:07Хорошее сравнение. Только это называется моральным релятивизмом и предполагает, что люди не являются сознательными существами способными к познанию окружающего мира. Что, судя по всему, слегка не верно. Хотя, если предположить отсутствие объективной реальности, то всё ok.
Shamov
17.06.2016 06:39Предположить можно было бы наличие объективной реальности. А её отсутствие, как бы, само собой подразумевается в ситуации, когда наличие не доказано. Но в любом случае это всё не важно. Быть сознательным существом и познавать окружающий мир можно вне зависимости от того, насколько этот мир объективен.
napa3um
16.06.2016 15:44Технари-редукционисты пытаются свести философию к набору атомарных правил вывода или к каким-то фундаментальным законам (мыслят в механистической парадигме ньютоновской физики), и терпят поражение ввиду того, что сама попытка такого наивного сведения — уже предмет философии. Философия — это о том, как и почему люди думают обо всём, о поисках критериев эволюции мысли, а не о каких-то конкретных таких критериях, выбранных в качестве догматов (аксиом, постулатов). А вот выбранные конкретные аксиомы и постулаты порождают различные науки, «прикладные» результаты философии.
alsii
16.06.2016 16:02+2Это хорошо. Ну и как и почему люди думают? Какие критерии эволюции мысли удалось найти? Под "атомарными правилами вывода" вероятно имеется в виду логика? Философия не нуждается в логике?
napa3um
16.06.2016 16:07-1Попробуйте порассуждать об этом сами или почитайте соответствующую литературу. Меня подобное обсуждение с вами вряд ли заинтересует.
Unrul
17.06.2016 01:40Под атомарными правилами вывода, видимо, имеются ввиду ранние попытки философов свести доказательство истинности какого-либо утверждения к механическому применению правил вывода, чтобы прийти к априорным аксиомам. Увы, ничего дельного не получилось.
taujavarob
17.06.2016 14:09Unrul > имеются ввиду ранние попытки философов свести доказательство истинности какого-либо утверждения к механическому применению правил вывода, чтобы прийти к априорным аксиомам. Увы, ничего дельного не получилось.
Почему не получилось? — Был такой теолог — Райму?нд Лу?ллий — он одной логикой(!) обращал мусульман и иудеев в католицизм в 12 веке (атеистов тогда не было, были ещё местами язычники). — Так он интересную механическую машину выводов то изобрёл! ;-)
Shamov
16.06.2016 14:41Вовсе нет. Строго говоря, объективной можно считать такую реальность, которая могла бы существовать вне какого-либо её описания на некотором языке. Я отрицаю лишь такую строгую объективность. Однако вполне очевидно, что может существовать такое описание реальности, которое нескольким людям будет казаться убедительным. Т.е. оно не будет противоречить их крайне ограниченному личному опыту. В рамках коммуникации между такими людьми, разделяющими общее описание реальности, можно для простоты считать это описание объективным. Но это всё очень условно.
napa3um
16.06.2016 14:47Похоже, вы, как и многие, путаете понятия «объективность» (независимость от субъекта) и «истинность» (непротиворечивость и однозначность в некой выбранной формальной системе). Ваши «очевидности» и «убедительности» к объективности не имеют никакого отношения.
Shamov
16.06.2016 15:09Ну, всё дело в том, что я не настоящий философ. Я просто выучил кое-какие умные слова.
Azoh
16.06.2016 14:49>объективная реальность перестала быть предметом научного метода
Для начала она никогда не была. Научный метод — совокупность инструментов изучения. Можно было бы предположить, что «объективная реальность» — это «предмет» изучения науки. Только с соответствии с «научным методом» предметом изучения науки является «реальность наблюдаемая». А это, как говорят, две большие разницы.napa3um
16.06.2016 14:52«Наблюдаемую» научным методом реальность принято называть объективной. Объективная реальность — это не совокупность (или усреднение) субъективных (наблюдаемых). Объективная реальность как раз та, что не зависит от субъективных суждений. Вне зависимости от того, что субъективное представление об этой объективной реальности формируется исходя из субъективных суждений.
Azoh
16.06.2016 16:34Тут нужно заметить, что для применения научного метода нет никакой разницы, есть ли объективная реальность или нет. Но это не мешает в вашей субъективной реальности считать то, что она не только существует, но и является предметом изучения науки.
napa3um
16.06.2016 16:38Для применения научного метода придётся считать предмет применения объективным, иначе получится бессмысленная операция с бессмысленным результатом. Вне зависимости от вашего непонимания научного метода и сути объективной реальности.
Azoh
16.06.2016 19:37Ладно, вопросы существования «объективной реальности» я считаю вопросами того же порядка, как и вопросы существования богов, так что не могу сказать, что глубоко всем разумом понимаю проблематику объективности реальностей. Однако же интересно мне, что именно в научном методе требует что-то от предмета изучения. Это же, по сути, набор правил того, как правильно из набора фактов делать модели (по сути вводить постулаты), а так же того, как правильно ставить эксперименты для получения новых фактов
napa3um
17.06.2016 07:38Каузальность и независимость от субъекта необходимы научному методу для производства _передаваемых_ знаний. Вопрос существования объективной реальности — это вопрос о наличии таких свойств у наблюдаемой людьми реальности, и в рамках самого научного метода наличие таковых свойств недоказуемо, а постулируется как данность. Свои личные откровения можно сколь угодно пытаться рационализировать научным методом, но если знания останутся в непередаваемой форме (не будут абстрагированы от субъекта в форму универсального эксперимента), это останется лишь фантазией, а не «смотрите, мне ничто не мешает использовать научный метод к собственным мыслям в рамках солипсизма».
alsii
16.06.2016 15:27+1Меня это всегда забавляло. Если реальность существует лишь как представление о ней некоего разума, объективен ли сам этот разум? Если да — то он уже является объективнйо реальностью. Если нет, то мы впадаем в бесконечную рекурсию.
Shamov
16.06.2016 15:45Декарт эту рекурсию прервал своим «Я мыслю — значит существую!» У любого сторонника картезианской философии теперь нет никаких проблем с этим.
alsii
16.06.2016 15:58А как с этим справляются остальные? Следует ли из этого рассуждения несостоятельность не признающих этого учений?
Shamov
16.06.2016 16:17Да начхать на остальных. Современная западная цивилизация построена на картезианском учении. Так что у любого, кто с ним солидарен, всё будет хорошо. А на остальных наплевать. Они имеют право на своё особое мнение. Но говорить с ними особо не о чем.
alsii
16.06.2016 16:41+2Ага. Уже третий раз встречаю сегодня этот ход: "Не согласен? Не понимаешь? Продолжаешь задавать вопросы? Говорить с тобой нре о чем!". Все-таки философия сильно отличается от естественных наук. И даже от неестественных :)
napa3um
16.06.2016 16:45А ваши наивные запросы пруфов, конечно, вы считаете сутью научной деятельности. Подумайте, насколько ваше мнение о «научности» имеет экспертную ценность (и вообще ваши представления о том, что философия должна укладываться в эти критерии). Над вашей наивностью и философ, и физик посмеются.
alsii
16.06.2016 17:37+1Да я не настаиваю на пруфах. Готов поверить вам на слово :-) Вот в данном конкретном случае. Спрашиваю, как решают этот вопрос другие направления философии? А мне в ответ: да гачхать на них. Я это понял так: "Нечего их вообще слушать, они ничего не понимают". Получается примерно так. Я сейчас сформулирую для себя некий набор утверждений: "Красное — это синее", "мягкое — это твердое", "зеленое не бывает угловатым". Все. Я философ. На всех, кто думает иначе: нчхать. Второе утверждение противоречиво? "Мы, философа не нуждаемся в логике. Логика сама предмет нашего изучения. Мы можем выдумать сто разных логик с парадоксом и кванторами". Если какие-то невежды спрашивают, что получится если взять угловатое и покрасить в зеленый цвет, то мы их посылаем читать книжки, ибо нечего тратить наше драгоценное время. Вообще весело, но какой от такого подхода практический результат? Ну и на религию еще смахивает...
napa3um
16.06.2016 17:52Какая вам разница, на что это смахивает и на то, можно ли называть бессмысленный набор тезисов философией? Как сформулируете практические критерии такой разницы, так и научитесь формулировать правильные («философские») вопросы, ответы на которые заставляют принимать то или иное допущение о свойствах реальности. И да, религиозные догматы от научных постулатов отличаются не по каким-то формально-логическим критериям, а исключительно по своему приложению. Догма, аксиома, постулат, допущение — это синонимы, обозначающие базовые предпосылки для последовательности умозаключений в рамках любой дисциплины, даже богословской.
alsii
16.06.2016 18:12+2Научные постулаты отличаются от религиозных догматов тем, что они фальсифицируемы. Кстати, являются ли фальсифицируемыми принципы, положенные в основу философии? В философии вообще существует понятие "доказательство"? Мне всегда казалось, что нет.
napa3um
16.06.2016 18:47Научные постулаты приходят из философии и в рамках научного метода фальсифицированы быть не могут, на них базируется весь корпус выводов в данной науке. Попробуйте доказать существование объективной реальности или попробуйте опровергнуть научный метод.
Shamov
16.06.2016 16:56Основное отличие состоит в том, что философия пытается ответить на другие вопросы. По большому счёту, существует лишь три главный вопроса: «что сделать?», «зачем это делать?» и «как это сделать?» Все реальные проблемы в конечном итоге сводятся к ним. Естественные науки пытаются ответить лишь на вопрос «как что-то сделать?» Философия же берёт на себя два других вопроса. Она, во-первых, формулирует саму проблему, которую нужно решить. Часто бывает так, что проблема вовсе не очевидна. Во-вторых, она может сформулировать мотивационную часть, ответив на вопрос «зачем?». Причём сформулировать достаточно убедительно для того, чтобы нашлись реальные люди, готовые тратить на решение проблемы свое время и силы.
alsii
16.06.2016 18:20+1Формулировка проблемы, это первая часть любой студенческой работы, не говоря о более. Так что тут у философии нет монополии. Вопрос "зачем" так же всегда решается. Например это может быть "технико-экономическое обоснование" или еще что-то. Да что там наука. Каждый из нас решает эти вопросы по 100 раз на дню: "Что то мне скучно… Не пойти ли мне в кино? или может лучше пива выпить...". То же у И. Маска: "Что-то мне скучно. Не создать ли мне платежную систему? Ну вот, теперь денег дофига… Не полететь ли мне на Марс? Или лучше сделать электрическую машинку? А пофиг… Пусть будет и то и другое". Теперь я вижу разницу. Маск — философ, а я, увы, нет :(
Shamov
16.06.2016 18:59Маск, безусловно, философ. В этом можете даже не сомневаться. Ещё у Платона была идея о том, что миром должны править философы. Именно это и случилось. Никто просто не заметил. Поскольку философы сейчас выглядят не так, как в Древней Греции. Они сейчас ходят в костюмах и работают советниками президентов, консультантами владельцев крупного бизнеса, а иногда и сами мутят какой-то бизнес. В общем, выполняют ту самую работу, которую можно назвать «управление миром».
alsii
16.06.2016 19:03В общем то я ничего против такого устройства мира не имею. Беда только в том, что есть еще другие философы, одеждами похожие на древнегреческих. Вот эти действительно мутят, и увы, не бизнес :(
Shamov
16.06.2016 19:10Вы имеете в виду профессиональных философов? Среди них тоже есть нормальные ребята. Но, действительно, многие из них говорят на каком-то непонятном языке и страшно далеки от народа.
Unrul
17.06.2016 01:52+1Философии бывают разные. Проблема в том, что большая часть современной философии сильно оторвалась от реальности и занимается чем-то странным. Чтобы избежать критики, эта часть использует гордыню, запутанные формулировки и свой диалект языка. И такое состояние является аттрактором из которого очень сложно выбраться. Если ты думаешь, что реальности не существует, то ты можешь оправдывать и допускать абсолютно что угодно.
napa3um
16.06.2016 16:01«Cogito, ergo sum» — это о пределе убеждения в собственной рациональности («непогрешимости умозаключений»), а не о существовании объективной реальности. Любой выдуманный литературный персонаж по Декарту существует уже только потому, что может себе в этом признаться, например.
alsii
16.06.2016 16:07+1Тогда это не разрыв рекурсии. Это просто переопределение термина "существую". Т.е. вопрос остается.
napa3um
16.06.2016 16:12Всё верно. Вопрос о существовании объективной реальности пока остался без однозначного ответа, польза принятия её существования является «самоочевидной» и не требует обоснования (т.к. альтернативные взгляды получаются всегда менее мощные в предсказательной силе). Объективная реальность существует потому, что существует, но если хочешь — попробуй отказаться от неё. Не смог? Тогда ешь что дают.
Shamov
16.06.2016 16:26Внутри реальности, создаваемой воображением читателя, выдуманный литературный персонаж, безусловно, существует. Он не существует в нашей с вами реальности. Но в ней он и мыслить может. Так что всё чётко.
napa3um
16.06.2016 16:28Но без чёткого разделения субъективной (создаваемой воображением читателя) и объективной (существующей вне зависимости от существования читателей) реальности. Так что не всё чётко.
Shamov
16.06.2016 16:46Ну, проблема разделения и его чёткости — это субъективная проблема. Будем исходить из ваших предпосылок о том, что объективная реальность существует. Представим себе два камня, которые лежат на земле. Вы не поверите! Им абсолютно начхать на то, различаются ли они чем-то между собой. Они об этом не думают. Потому что вообще не думают. Так же и с реальностями. Различие между субъективной реальностью литературного произведения и тем, что вы называете объективной реальностью, актуально только для читателя, который думает об этом.
С практической точки зрения удобно пользоваться категорией «различимое различие» (difference that make a difference). Когда субъект видит разницу между А и B, которую может как-то описать на своём языке, считается, что разница есть. Если же субъект разницы не видит или не может её описать, то значит и разницы никакой нет. Например, две картофелины примерно одинакового размера имеют разную форму. Это очевидно. Тем не менее, тому, кто будет есть картофельное пюре, это различие безразлично. Для него различия, как бы, и нет.napa3um
16.06.2016 16:51Не очень ясно, что вы этим пытались аргументировать. «У каждого своя правда» — действительно, такой софизм невозможно опровергнуть, но каких-то конструктивных выводов из этого сделать тоже не получится.
Shamov
16.06.2016 17:05Это не софизм, а суровая правда жизни. Конструктивный вывод состоит в том, что бороться с этим состоянием дел не нужно. Люди могут сотрудничать друг с другом не только тогда, когда у них одна общая правда. Достаточно сойтись в каких-то отдельных моментах, важных для каждого из них. Те же различия в описаниях реальности, которые не имеют для них значения, можно считать несуществующими. В результате объективной реальности нет, а взаимовыгодное сотрудничество есть.
napa3um
16.06.2016 17:11Не уверен, что ваши личные конфликты и проблемы с работой в команде имеют какое-то отношение к проблематике обоснования существования объективной реальности. Хотя подобное и довольно распространено — так называемая интеллектуализация своих личных переживаний путём формулирования их в наукообразной обобщающей (далеко не всегда корректно обобщающей, но дистанцирующей) терминологии.
alsii
16.06.2016 16:52+1Вот это правда очень интересно. Что означает "существование" внутри воображаемой реальности? Насколько я понимаю, если реальность воображаемая, в ней может существовать все, что угодно. А раз может, значит существует? В таком контексте само понятие "существования" теряет смысл.
Shamov
16.06.2016 17:16Не теряет. Потому что существование изначально имеет смысл только в определённом контексте. Нечто существует не вообще, а как что-то такое, что можно как-то описать. Например, человек может существовать как чей-то муж. Или как сотрудник какой-то компании. Или как фрилансер. Идентичность человека — это пересечение всех возможных описаний, которые к нему применимы. Если вас, например, попросить ответить на вопрос: «Кто вы?», вы начнёте перечислять все эти описания, которые имеют для вас некоторый смысл. И, возможно… только возможно… они ещё имеют смысл для других людей, которые точно так же существуют в той же системе отношений.
napa3um
16.06.2016 17:18Перестаньте, вы явно не в теме. Вы наличием множества субъективных реальностей пытаетесь обосновать отсутствие объективной, что, конечно же, просто смешно.
Shamov
16.06.2016 17:40Все позитивисты мира сейчас виртуально смеются на вашей фразой «обосновать отсутствие». Любому позитивисту с детства известно, что отсутствие обосновать невозможно, и потому обосновывать всегда следует только наличие. И до тех пор, пока вам не удастся обосновать наличие объективной реальности, у меня даже никакой проблемы с этим нет. Поскольку всё, что вы говорите, — это ни о чём. (Простой постулат о наличии я обоснованием не считаю.)
napa3um
16.06.2016 17:58Сформулируйте, пожалуйста, вашу точку зрения парой-тройкой тезисов, ибо я не понимаю, к чему относится ваша аргументация, и о чём вы продолжаете беседу.
Shamov
16.06.2016 18:18Могу даже одним тезисом сформулировать. Простое постулирование существования столь спорной вещи как объективная реальность — это читерский приём. В рамках философских рассуждений такой приём вполне годится. Но гордиться тут особо нечем.
napa3um
16.06.2016 18:53Т.е., ваш тезис заключается в том, что философам нечем гордиться? Они наверняка не переживут вашей оценки, хотя и не очень понятно, зачем этот тезис вы адресуете мне.
Философы могут гордиться результатами всего научно-технического прогресса, т.к. именно в философии сформулирован научный метод и его предмет исследования, которые привели к рассвету всех ныне существующих наук. Если уж на то пошло. Но вы, кажется, уже забыли о сути беседы (мы об объективной реальности, насколько помню, беседовали).
Shamov
16.06.2016 19:05Вот только не надо переоценивать научный метод. Это вполне нормальный инструмент для верификации гипотез. Только, к сожалению, сами гипотезы он порождать не может. Они всегда появляются в голове у исследователя каким-то ненаучным способом. В результате интуитивного прозрения или ещё как-то.
napa3um
16.06.2016 19:26Мне казалось, что переоценили его вы сами. У вас какая-то своя личная война с философами, в которую вы, кажется, пытаетесь меня втянуть, и которая, должен признаться, мне совсем неинтересна.
Shamov
16.06.2016 21:03Я вас умоляю! Какая у меня может быть война с философами как таковыми? Я им глубоко симпатизирую. Другое дело, что я не считаю позитивистов адекватными нашему времени. Такой кондовый позитивизм, который вы пытаетесь выдать за актуальную философскую концепцию, с объективной реальностью и прочими удобными допущениями, в нашу постмодернистскую эпоху — это уже даже не ретро, а архаика…
napa3um
16.06.2016 21:08По-вашему, ваш солипсизм свежее и актуальнее. Ваше резонёрство очевидно.
Shamov
16.06.2016 21:21Если моя каша из некорректно используемых терминов лучше всего матчится у вас с солипсизмом, то это просто ваша ошибочная интерпретация. На самом деле, я просто пишу кашу из некорректно используемых терминов. У неё нет общеизвестного названия.
alsii
16.06.2016 18:02+1погодите, описание подразумевает существование некоего субъекта, который описывает. Тогда выходит, что объективно ничто не существует. Далее, как быть с существованием того, о чем ни одному субъекту неизвестно. Соответственно нет и описаний. Оно существует? Дальше. Если есть описание, можно ли утверждать, что существует и сам объект? Например существует ли "Чайник Рассела"? Или невидимый розовый единорог?
Shamov
16.06.2016 18:47Вопрос в том, что такое объективное существование. Существовать объективно — это существовать как что? Как это можно описать, не используя в описании концепцию объективности, которую придумал Декарт? (Не открыл, как открывают законы физики, а именно придумал и ввёл в обиход.) Как только вы сообщите мне, что вы имеете в виду под объективным существованием, я сразу же смогу сообщить вам, существует ли что-нибудь в этом мире объективно или нет.
Существование того, о чём никто не знает, находится под вопросом. Нельзя сказать ни да, ни нет. Многие вещи существуют как чистый вымысел. Некоторые существуют как мифы. Кто-то верит в их существование, а кто-то не верит. Но обязательно нужен кто-то, кто хоть что-то об этом знает. Чайник Рассела существует, как минимум, в качестве мема. Невидимый розовый единорог тоже в некотором смысле существует. Хотя бы как пример невозможного. Вообще абстрактные категории могут существовать без всяких ограничений. Параллельное существование реальных прототипов необязательно. Есть один очень мощный инструмент абстрагирования — отрицание. Например, существуют сигареты. Все понимают, что это такое. А теперь попробуйте себе представить реальный прототип абстракции под названием «не сигарета». Не какую-то конкретную вещь, которая бы не была сигаретой, а именно «не сигарету» вообще. Это что такое? Как это выглядит? Оно видимо или невидимо? Ответов на эти вопросы нет. Потому что это чистая абстракция. Так же и невидимый розовый единорог. Это абстракция. Пример невозможного в реальном мире. Именно в этом качестве оно и существует. Только в сознании людей.
VolCh
16.06.2016 15:15Мы уничтожаем состояние «холодная» и создаём состояние «горячая» одной и той же (потерями на испарение и т. д. пренебрежём) воды. Мы можем (часто без усилий с нашей стороны) даже уничтожить состояние «горячая» и создать новое состояние «холодная», но это будет не то же состояние, а новое. Неотличимое по значению свойства «температура», но с новой идентичностью — нельзя дважды войти в одну и ту же реку.
Shamov
16.06.2016 15:29Состояние — это абстракция. В реальности оно не существует. Просто мы с вами владеем некоторым языком, который позволяет нам выразить эту абстрактную концепцию так, чтобы она была применима к воде. Выражения «уничтожаем состояние» и «создаём состояние» весьма условны. Эти создания и уничтожения происходят исключительно в нашем сознании, а не в реальном мире.
VoidEx
16.06.2016 15:50Так «один и тот же объект» — тоже условность.
Shamov
16.06.2016 16:31Я даже больше скажу. Вообще всё — условность. Даже сама условность — это тоже условность. Вопрос лишь в том, на каком уровне условности выбрать точку опоры, чтобы можно было от неё оттолкнуться и сделать что-то продуктивное. Хотя продуктивность — это, конечно, тоже условность.
napa3um
16.06.2016 16:33Это называется софистикой, и из подобных рассуждений никаких конструктивных выводов сделать, как известно, не получится.
alsii
16.06.2016 16:36+1Эту фишку в юриспруденции любят: "Ваша честь! Стоящий перед Вами человек, вовсе не тот, который совершил это ужасное преступление! Тот негодяй, умер там, вместе со свойе жертвой! Подвергнуть же наказанию этого невинного агнца, с совершенно иным отношением к жизни и к людям, означало бы совершить преступление еще более бесчеловечное". ;-)
VoidEx
16.06.2016 16:44+1А он и есть другой, однако свойства нового агнца сходны с исходным, а мы хотим породить нового агнца, с отличными свойствами, для чего вынуждены предпринять некие действия :)
VoidEx
16.06.2016 15:49Если вода выкипает, а я периодически доливаю холодной — у меня там одна единственная вода в состояниях «выкипает» и «нагревается, температура t», или разные воды?
ApeCoder
16.06.2016 09:57+11Никогда так не пишите статей — автор попробовал реализовать что-то в функциональном стиле на нефункциональном языке, ему стало больно, он об этом написал. Без какого-то анализа причин и альтернативных решений.
На Haskell то, что он написал, выглядело бы примерно так:
parseVarant = parseA <|> parseB <|> parseC <|> parseD
GrizliK1988
16.06.2016 10:12+1Любопытства ради: а если парсеров неопределенное количество?
VoidEx
16.06.2016 10:18+4Неопределённое в каком смысле? Если список парсеров, или если вы к готовому хотите ещё добавить вариант?
someParser = foldr1 (<|>) [parseA, parseB, parseC, parseD] someParser' = someParser <|> parseX
соответственно
Bas1l
16.06.2016 11:55+2Ну, я перевел ее скорее из-за примера с пирогом, потому что он показался мне очень точным (и веселым, и доступным). В том смысле, что функциональные языки, действительно, пожалуй, далеки и от тех моделей задач и реального мира, что в голове у программиста при написании программы, и от того, как работает компьютер.
Source
16.06.2016 13:53-3Попробуйте написать реальную программу, которая печёт пирог. Рецепт — это ближе к ТЗ, чем к программе. И компьютер так не работает.
kmikeru
16.06.2016 14:10Но есть же и другой пример, «Паблик статик файнал Борщ борщ нью Борщ», разве это ближе к задачам и реальному миру?
0xd34df00d
17.06.2016 04:56+1Автор на нефункциональном языке не с той стороны зашёл.
Сначала берётся boost.optional, потом пишется тайпкласс MonadPlus со всей своей иерархией, потом msum. И всё.
Zealint
16.06.2016 10:22+1Есть ещё одна причина непопулярности функциональных языков, по крайней мере, в сфере сложных научных расчётов. На функциональном языке нельзя написать ни одной эффективной (и одновременно полезной) программы, которая выжимала бы из процессора максимум. Это можно легко сделать на Си, на Паскале (и Delphi) в связке с ассемблером… на Java и C#, к сожалению, нельзя, ещё можно на Фортране. Да, можете со мной спорить, если есть опыт и аргументы, но факт таков: ни одна из сложных задач, по крайней мере, из моего списка задач, не будет решена на функциональном языке. Они все будут решены на простых императивных языках с помощью кластеров и видеокарт.
Да, я знаю, что тут не нужно путать язык и компилятор, ведь эффективность кода зависит от компилятора. Однако я пока не видел компиляторов функциональных языков, которые давали бы не тормознутый код.Oxoron
16.06.2016 10:31-2Можно сделать финт ушами. На хабре проскакивала статья, в которой человек создал сайт с майнинговым кодом на JS. Эффективность подхода зависит от популярности сайта, но потенциально гораздо выше одного процессора и видеокарты.
youlose
16.06.2016 11:17-2«Они все будут решены на простых императивных языках с помощью кластеров и видеокарт. „
Как раз в этой-то области и цветёт функциональное программирование как парадигма. Ибо чтобы распараллелить задачу надо произвости декомпозицию до простых частей, а это и есть суть функционального подхода.
Да и чисто императивных языков сейчас всё меньше и меньше (а может быть и вообще не осталось), сплошь и рядом пытаются расширять их для написания в функциональной парадигме.Bas1l
16.06.2016 11:48+7На суперкомпьютерах обычно поддерживаются только три компилятора: С, С++ и Fortran. Параллельность—за счет Message Passing Interface плюс OpenMP. Так что там, по крайней мере, функциональное программирование точно не цветет.
youlose
16.06.2016 13:21-2Я ответил про кластеры и видеокарты, про суперкомпьютеры речи не было. И неважно через что вы работаете, если появляется иммутабельность данных (а при передаче сообщений именно так и происходит в основном), то это уже заслуга парадигмы функционального программирования.
Idot
16.06.2016 11:23-5на Паскале (и Delphi) в связке с ассемблером…
Так на любом в связке с ассемблером можно! lol
C#, к сожалению, нельзя
Читал рекомендации по настройке C# с отключением кучи всего и получением результата по скорости близкого к C++ Net.
На функциональном языке нельзя написать ни одной эффективной (и одновременно полезной) программы, которая выжимала бы из процессора максимум. Это можно легко сделать на Си
Сам не проверял, но читал про следующий трюк:
— берём функциональный код f(a,b)
— а затем преобразуем получая вот такое a b f
=> получился Forth! Который, по скорости вполне сравним с C => PROFIT!Bas1l
16.06.2016 11:46+3Так надо же с C++ сравнивать, а не с C++ .Net. Конечно, с другим—таким же медленным— .Net языком сравнение будет хорошим. Там была, скорее всего, та же самая garbage collection, в первую очередь.
Idot
16.06.2016 12:46на Паскале (и Delphi) в связке с ассемблером…
Так на любом в связке с ассемблером можно! lol
-1
Если есть что возразить — возражайте!
И тот и другой языки — не быстрые (сам на них долгие годы писал, в случаях когда скорость исполнения не требовалась), а возможность влепить вставку на Ассемблере — не делает сам язык быстрым.
eoffsock
16.06.2016 14:20OCaml? Ну, он ООП конечно, но считается функциональным, и с крутым оптимизирующим компилятором. По данным зарубежных и российских коллег, обгоняет по производительности плюсы, как минимум.
staticlab
16.06.2016 14:20-1А шейдерная программа не есть чистая функция, которая применяется (map) ко множеству фрагментов?
redbeardster
16.06.2016 14:20-1Как аргумент contra — fprog.ru/2010/issue4/vitaly-mayatskikh-lisp-abstractions-on-steroids
Akon32
16.06.2016 14:20А какие у вас задачи?
У меня вполне получалось писать т.н. «научные расчёты» на Scala, и если иммутабельностью не упарываться (т.е. тыкать всюду массивчики и мутабельные хэш-таблицы) и память очень часто не выделять, работает всё сносно. OpenCL вполне подключается, и кластеры наверно тоже можно задействовать (мне не приходилось).
Delphi, насколько я помню, выдавал не слишком уж оптимальный код (до внедрения llvm точно) по сравнению с GCC, и язык/экосистема имхо куда более непроработаны, чем в прочих популярных языках. Если что-то распараллелить нужно — одним .par или #pragma omp parallel for не обойдёшься. В том же С++ с этим проще. По моему опыту, «эффективный код» и «Delphi» — несовместимы, даже странно слышать эти понятия вместе.Idot
16.06.2016 19:14+1По моему опыту, «эффективный код» и «Delphi» — несовместимы, даже странно слышать эти понятия вместе.
Смотря, что именно считать эффективностью. По скорости — он, да, не быстрый, и для этого лучше C и C++.
Но, он крайне эффективен для Rapid Aplication Development — когда нужно быстро получить работоспособный ясно читаемый код работающий без ошибок.
Zealint
16.06.2016 20:06+1Знаете, самые разные из класса так называемых «труднорешаемых» задач. Которые в принципе не имеют эффективного алгоритма решения. Классический пример: число незамкнутых маршрутов шахматного коня на доске 8x8. На данный момент считается нерешённой проблемой комбинаторики, однако в моей собственной классификации она находится где-то посередине по сложности. В принципе, решается она не очень сложно, но ни один функциональный язык её не потянет. Можно даже не пытаться. Решать её нужно на кластерах только на Си или Фортране. Конечно, я не говорю о том, что сам программист при этом должен быть очень сильным.
Не буду объяснять то, зачем решать такие задачи, но если кратко, то их решение открывает в науке новые методы, с помощью которых решаются аналогичные задачи, скажем, из статистической физики (например, расчёт термодинамических свойств макромолекул, этим я занимался в своей диссертации).
Я думаю, что нет смысла доказывать мою точку зрения, просто выскажу общую мысль всех тех учёных, которые трудились над подобными задачами: функциональщина там не пройдёт ни под каким видом. А нравится это кому-то или нет — нам без разницы: ) Не пройдёт и всё. Никто пока не смог доказать обратного на реальных практических примерах.Akon32
17.06.2016 18:45Было бы интересно взглянуть на код. (безотносительно споров о функциональщине и языках, тут я наверно соглашусь с вами)
Zealint
17.06.2016 20:33Какой код?
Akon32
18.06.2016 09:43Реализации решения этих ваших задач (если он открытый, естественно).
Zealint
18.06.2016 10:19Это невозможно по целому ряду причин. Одна из них (не самая важная) — весь свой научный код я удалил, когда ушёл из академической науки, осознав за несколько лет трудов, что там ловить нечего. Но есть и другие причины, по которым я всё равно бы его не показал. Самая важная причина — в научных расчётах, особенно новых, не решённых ранее задач, важно, чтобы кто-либо повторил результат независимо. Ответы должны сойтись. Когда даёшь свой код кому-то, гарантия независимости обнуляется, а это недопустимо. Возможно, я ещё вернусь к этим задачам, но без какой-либо связи с государственной наукой.
0xd34df00d
18.06.2016 17:22+3весь свой научный код я удалил
Но зачем так делать?Zealint
18.06.2016 20:01+2Затем, что мне он не нужен. «Учёным», на которых я работал, как выяснилось, тоже, а хранить хлам не люблю. Если вернусь к этим задачам, то в любом случае буду все переписывать начисто, уже с иных позиций и имея больше опыта. Вообще, в моей концепции научной работы программы переписываются с нуля десятки раз, я против классической схемы, когда код пишется один раз и потом только исправляется и дорабатывается, эта позиция не работает в сфере сложных расчётов. Шаг за шагом, удаляя старую программу и создавая новую, мысль совершенствуется. Старая программа даёт некий набор тестовых данных для новой. Новая даёт набор ещё больше — и так далее десятки раз. На третьем четвёртом шаге обычно получается новые научные результаты, но для того, чтобы найти свой предел, итераций требуется гораздо больше. Зачем удалять старую программу? Чтобы не было соблазна забрать оттуда кусок (скопировать или просто подсмотреть, когда забыл), перетащив в новую программу какие-то недостатки.
bentall
16.06.2016 10:23+4Императивное программирование (ага, в терминах рецептов/иструкций) и декларативное (в терминах математических определений/стратегий) — два разных способа мышления. Поначалу при обучении программированию математиков (а когда всё начиналось, программировать учили именно их) отучали мыслить математически и учили мыслить алгоритмически. Всерьёз что-то меняться начало только в XXI веке (хотя по разному бывает, SICP вот, наоборот, больше не преподают, хотя он, конечно, не так чтобы совсем уж радикально декларативен).
Когда в продакшен придёт поколение разработчиков у которых эту мышцу в мозгу наоборот развивали можно будет поговорить, насколько «чистое» функциональное программирование подходит для решения реальных задач и появятся какие-то наработки по оптимальным способам декомпозиции задачи/описании архитектуры ПО. А пока, очевидно, об этом говорить рано.Idot
16.06.2016 11:39Вроде, эффективное функциональное программирование требует отличного знания Лямбда-исчисления и прочего мат.аппарата => чтобы пришло такое поколение разработчиков необходимо чтобы в ВУЗах такому массово обучали. Вот лично, у меня в ВУЗе не было такого чтобы мы изучали Лямбда-исчисление и тому подобное. Интересно, сколько человек на Хабре могут похвастаться тем, что изучали в ВУЗах нечто подобное (было бы хорошо провести опрос).
lockywolf
16.06.2016 13:33+5У меня было лямбда-исчисление в ВУЗе (МФТИ) в качестве одной из модели вычислений. Никак мне это не помогло освоить ФП.
У нас даже курс по ФП был (на Окамле), на котором мы поупражнялись во впихивании императивно очевидных задач в ФП, что вызвало немало боли и отвращение к предмету.
На самом деле, больше всего разобраться со всем этим мешало именно непонимание базовой структуры данных. В императивном языке это массив, тривиально отображающийся к плоской памяти Фон-Неймана. (Или, например, на лист бумаги с ручкой.)
Хочешь — пиши в него, хочешь — исполняй его пошагово.
А в ФП какая базовая структура данных? Связный список? Тот самый, в котором доступ по индексу за O(n) осуществляется?Flammar
16.06.2016 14:06Односвязный список;-) который хорош прежде всего тем, что к одному «хвосту» можно «присобачить» несколько «голов», благо всё реализуется через указатели. А не нужные «головы» потом уберёт сборщик мусора.
0xd34df00d
17.06.2016 04:58Опа, а вы какой факультет и год выпуска? У меня на фупме с 2014 годом не было :(
Shamov
16.06.2016 11:02+6Если бы ему нужно было приготовить не один пирог, а, скажем, десять тысяч одинаковых пирогов силами 946 пекарей, в распоряжении которых имеются 234 духовки (из которых одновременно в сеть можно включить только 178)… 534 противня… 421 миска… 765 миксеров… 124 измельчителя для орехов и… 511 полотенец для остывания, то ему бы в любом случае пришлось преобразовать рецепт к функциональному виду, полностью отделив шаги приготовления от текущего состояния пирога. В противном случае пекари тратили бы слишком много времени на непродуктивное ожидание между выполнением отдельных шагов алгоритма.
Просто нужно перестать совать всем в лицо это ваше функциональное программирование, а просто тихо и спокойно использовать его в тех немногочисленных специфических задачах, на которых у этого подхода есть очевидные преимущества. И тогда всё у него будет хорошо. Оно будет фантастически популярно… в узких кругах.Idot
16.06.2016 11:55А Event-Driven программирование разве в этом случае не подойдёт?
— Event освободилась ёмкость для замешивания теста => месим тесто
— Event освободилась форма пирога => заливаем в него тесто итп
— Event освободилась духовка => ставим туда форму с пирогом
— Event пирог испёкся => освобождаем духовку и форму для пирога
ну и так далее в том же духе.
Причём всё логично и легко понятно, а на События можно реагировать процедурно.Shamov
16.06.2016 12:15+4Event-Driven — это, как бы, lite-версия ФП. Для тех, кто уже хочет отделить шаги алгоритма от состояния, но ещё не готов к хардкору.
В приведённом списке обработчиков событий каждый обработчик будет выбирать из общего пула пирог, который находится в определённом состоянии. Например, в случае обработчика освободившейся формы для пирога, нужно будет выбрать из пула пирог, который уже существует в виде замешанного теста и готов к выкладке в форму. Т.е. состояние пирога будет отделено от шагов алгоритма.
В одном из последних номеров Overload как раз была статья о том, как можно постепенно приучить нормальных программистов к функциональной парадигме через event-driven и конечные автоматы (http://accu.org/index.php/journals/2199).Idot
16.06.2016 19:44Спасибо за полезную ссылку.
PS Думаю, что если Вы переведёте, то получите плюсы в карму. :)Shamov
16.06.2016 21:09А я думаю, что у меня есть более интересные дела :) Но, на самом деле, автор статьи русскоговорящий. Возможно, у него можно попросить готовый русский текст и выдать его за перевод. Не исключено, что изначально статья была на русском, а на английский была переведена.
Idot
17.06.2016 04:00Я читаю, по английски (и не только читаю). Я про то что Вашей Карме на Хабре очень пригодились бы плюсы за перевод. :)
Shamov
17.06.2016 06:31+1Я не парюсь насчёт кармы. Я уже взрослый.
Idot
17.06.2016 07:24Можно было бы не париться, если бы на Хабре при падении Кармы, не возникали бы ограничения на количество сообщений — сначала 1 сообщение в час, затем 1 сообщение в день, потом 1 сообщение в неделю, и так далее. Если у Вас нет ни одной статьи, то Вашу карму можно только минусовать.
Shamov
17.06.2016 07:40Много лет я вообще не мог писать сообщения. Эта возможность по необъяснимой причине открылась совсем недавно. И ничего. Как-то жил с этим. Более того, так даже лучше было. Не нужно было ни писать комментарии, ни отвечать на ответы. Хорошее было время.
MaximChistov
17.06.2016 08:57+1>Если у Вас нет ни одной статьи, то Вашу карму можно только минусовать
Неа, можно плюсовать до +4Idot
17.06.2016 18:24Что-то впервые такое слышу. И не вижу, чтобы кто-то плюсанул Shamov'у за его полезную ссылку для желающих ознакомиться с ФП. Где такое в правилах?
MaximChistov
17.06.2016 18:33https://geektimes.ru/company/tm/blog/276278/
Да и проверить просто — у людей без публикации не только красная стрелочка теперь.
saluev
16.06.2016 11:03+3На секундочку, автор мог бы сделать
и обойтись без рекурсии. Страшновато, конечно, но еслиbool success = false; ParseResult<V> result; using expand_variadic_pack = int[]; // фокус! (void)expand_variadic_pack{0, ((success = success || ( parsers.parse(state).get_success() && ((result = ParseSuccess<V>{ {std::move(parsers.parse(state).get_success()->value)}, parsers.parse(state).get_success()->new_state }), void(), 1) )), 0)... };
parser.parse
принимаетconst State&
, то компилятор, возможно, даже соптимизирует три одинаковых вызоваparsers.parse(state).get_success()
в приведённом коде.
(Объяснение происходящего на SO)
PretorDH
16.06.2016 11:55Все сводится к архитектуре приложения.
Язык написания абсолютно не важен. Объектно-ориентированная модель может быть построена и на функциональном языке. В итоге всеравно все к этому сводится (машинный код).
Где проще использовать функциональный язык зависит только от сферы применения и расширяемости конкретной задачи.
Если надо написать статичное решение (Например: драйвер железа) которое не будет архитектурно расширятся внутрь, а только обростать новыми модулями, библиотеками или програмами. То его быстро и удобно написать на функциональном языке.
Но если есть задача построить виртуальную модель реального мира в базе-данных с множественными связями, то писать это на функциональном языке будет сложно по одной причине — исходная архитектура «трущебы». Их надо разобрать и собрать из их же деталей небоскреб. Получится дёшево но не эстетично, с оргомным превышением временного бюджета.
Для таких задач и образовалась специальность архитектор. И только он решает, что должно быть написано в функционально стиле, а что перенести в ООП. А специалист просто должен специализироваться на своем участке.
k12th
16.06.2016 11:59+5Функциональное програмирование странное по одной-единственной причине: оно… странное. Потому что в школах учат бейсику/паскалю, а в вузах плюсам и джаве.
Если взять, так сказать, Маугли от программирования и воспитать его исключительно на хаскеле/ML (предположим, лисп-машину кто-то построил и императивных процессоров наш лягушонок не видит), никаких проблем с ФП у него не будет, а когда ему в 25 покажут джаву, он, конечно, сначала малость прифигеет, потом скажет, что это придумали какие-то инопланетяне, потом проникнется и скажет, что это клевый способ решить некоторые проблемы, которые в хаскеле выходят не слишком красиво. И, в конце концов, придет к выводу, что лучше всего сочетать два подхода:)
Idot
16.06.2016 12:00Вообще-то, для обучения существует вполне функциональный язык Logo, и который вполне подходит для детей.
folkyatina
16.06.2016 14:38А чего это он стал функциональным и когда?
Idot
16.06.2016 15:15Потому что основан на LISP.
EvilBlueBeaver
16.06.2016 15:42А LISP у нас функциональный уже? И какой именно язык из семейства LISP? А то что-то в CL, например, объектно-ориентированности и императивности полным-полно
Idot
16.06.2016 19:16+2O_O а давно уже LISP перестал быть функциональным? *в офигении*
PS язык Logo появился в 1967 году на основе классического LISP.EvilBlueBeaver
16.06.2016 20:35Да, он мультипарадигменный, например, вот
(defparameter *my-hash* (make-hash-table)) (setf (gethash 'one-entry *my-hash*) "one")
или вот
(defclass person () ((name :accessor person-name :initform 'bill :initarg :name) (age :accessor person-age :initform 10 :initarg :age))) (setq p1 (make-instance 'person :age 100)) (setf (person-age p1) 101)
Да, в нем можно писать в функциональном стиле. Ровно так же можно писать и в каком-нибудь C#, но тот функциональным не становится.
Собственно даже из университетского курса ФЛП помню, что никто не заморачивался в лиспе с функциональщиной, рекурсиями, свертками и прочим. Все тупо бахали setf и циклы, а преподу было наплевать, ведь сказали же, что лисп функциональный, значит все нормально.Idot
17.06.2016 04:20+1Функциональность появилась в LISP раньше, чем появился сам C#, и LISP традиционно относят к функциональным языкам. И даже для него специально создавали «LISP-машины» именно специально для функционального программирования.
warlock13
16.06.2016 12:09+2Я не знаю, как перенести эти шаги в функциональный стиль без использования изменяемого состояния.
Может быть вы мало программировали на нормальных функциональных языках? У нас есть один изменяемый объект (пирог), над которым совершается множество действий, переводящих его из одного состояния в другое… да это же монада State как она есть!Bas1l
16.06.2016 12:38Справедливости ради, программировал не я, а автор оригинала, но ваш пример, на мой взгляд, созвучен идее статьи. Чтоб записать простой рецепт, мне нужно разобраться с монадами и с монадой State. Вы серьезно? То есть, получается, в обоих языках нужно будет выучить структуры/алгебраические типы данных (если типов пирога и т.п. под рукой нет). Нужно будет научиться писать или хотя бы использовать методы/функции (разогреть, смешать). Но в ФП еще вот, пожалуйста, теорию категорий, монады, монаду State. Вот в этом, собственно, и проблема (и причина даже описана в статье—ФП оказывается слишком далеко от реальности, от типичных моделей, с которыми работает мозг и от работы машины).
VoidEx
16.06.2016 13:16+1Во-первых, чтобы разобраться со State для практического применения, вовсе не надо «теорию категорий» и даже «монады» в математическом их смысле.
Во-вторых, вы, вероятно, полагаете, что всё, что вы перечислили, необходимое к обучению и в императивном языке, оно значительно проще, чем State, и потому добавление State — та соломинка, что переломит хребет верблюду?kreslavsky
16.06.2016 17:20Не так давно я как раз подбирал подходящую аналогию, чтобы объяснить отличие императивного подхода от функционального. И нашёл её в школьном курсе математики.
Все мы с вами знаем, кто такие синус и косинус. И нам не приходит даже в голову пытаться их применять через определение. Мы просто знаем и пользуемся. Но в школе вхождение в эту тему имело очень ненулевой порог для многих.
Так и в функциональном программировании: однажды въехав в тему (например, монада State), мы начинаем её просто использовать везде, где она нужна.
С применением императивного же подхода нам гораздо легче начать, оперируя базовыми конструкциями, но при этом мы каждый раз воспроизводим эти конструкции от той самой базы.
Итак, начнём.
def разогретьДуховку: (Духовка, T) => Future(РазогретаяДуховка) def подготовитьПротивень: (Противень, Мука) => Future(ПодготовленныйПротивень) def подготовитьОсновуДляТеста: (Мука, Сода, Соль) => Future(ОсноваДляТеста) def приготовитьЗаправку: (Масло, Сахар, КоричневыйСахар, Яйца, Бананы) => Future(Заправка) def приготовитьТесто: (ОсноваДляТеста, Заправка, Кефир, Орехи) => Future(Тесто) def выпечь: (ПодготовленныйПротивень, РазогретаяДуховка, Тесто, Время) => Future(ГорячийПирог) def остудить: (ГорячийПирог, Полотенце, T) => Future(ГотовыйПирог) let холоднаяДуховка = Духовка() let чистыйПротивень = Противень() let масло = Масло(450г) let сода = Сода(1ч.л.) let cоль = Соль(1ч.л.) ... etc let пирог = for { духовка <- разогретьДуховку(холоднаяДуховка,175) противень <- подготовитьПротивень(чистыйПротивень, Мука(50г)) основа <- подготовитьОсновуДляТеста(...) заправка <- приготовитьЗаправку(...) тесто <- приготовитьТесто(основа, заправка, Кефир(200г), Орехи(50г)) горячийПирог <- выпечь(противень, духовка, тесто, 30мин) пирог <- остудить(горячийПирог, Полотенце(), 25) } yield пирог
Сложно?
atc
16.06.2016 17:40+2Станет сложным, как только понадобится уведомлять интерфейс о прогрессе текущей выполняемой операции, например запекании и остывании, применять поправки к рецепту, в зависимости от указаний пользователя. То есть когда задача выйдет за рамки простого вычисления.
Так же свою часть сложности внесет описание структур данных, они станут или очень сложными или многочисленными, например реальная духовка может быть крайне сложна и иметь огромное количество параметров. Можно конечно ввести структуру данных «духовка для пирога» и функцию «разогретьДуховкуДляПирога», но это было бы не слишком удобным решением.kreslavsky
16.06.2016 18:10В данном случае задачу можно разделить на вычисления и эффекты. Для реализации необходимых эффектов мы просто используем соответствующую монаду. Или их комбинацию.
Что мы получаем:
- ключевая бизнес-логика процесса явно читается из кода
- вычисления соответствуют принципу Single Responsibility, легко тестируются и распараллеливаются
- каждый из эффектов реализован и оттестирован в одном месте — соответствующей монаде.
Если принять, что мы при этом использовали подход Domain Driven Design, то часть сложных эффектов явно выходит за рамки нашего Bounded Context и у нас сводится к публикации соответствующего Business Event.
Как-то так. Кстати, если мы про реальный мир, то там не обойтись без обработки ошибок. Добавим:
пирог .map(УпакованныйПирог(пирог)) .recover{ case Подгорел() => .... case Непропёкся() => ... case ОтключилиЭлектричество() =>... case НахамилНачальник() => ... default => ... }
Ну, вы понимаете. Опять же, логика читается из кода. Я за это очень ценю такой подход.
igrishaev
16.06.2016 14:13+1Автор оригинала допустил фатальную ошибку. Рецепт должен быть не композицией функций, вложенной структурой данных, например, списком словарей или пар (сущность — атрибуты). И отдельный код, возможно, с побочными эффектами, для извлечения результата их этой структуры.
Как справедливо заметили выше, императивный подход хорош только для одного пирога. Когда понадобится 100 пирогов при наличии 10 человек, 5 печек и 20 миксеров — начнутся проблемы с распределением ресурсов. Как функциональный подход справляется с этим, я писал в блоге.
csbubbles
16.06.2016 14:20+2Мне кажется, что идея функциональных языков как раз в упрощении описания задачи.
Типа:
(достать (печь (поставить пирог в (разогреть духовку до 175 градусов)) в течение 30 минут))
или
(достать (выпеченный (поставленный пирог в (разогретую духовку до 175 градусов)) в течение 30 минут))
Наиболее используемая скобочная нотация, возможно, не лучший DSL для описания задач, но, тем не менее, мне кажется, то, что описано в статье, не совсем правильно применять к реалиям функциональных языков. Я боюсь, что видение «головоломок» в функциональных языках происходит обычно от того, что люди пытаются применить к нему подходы, к которым они просто привыкли в процедурных или ОО языках. Но это как разговаривать на русском языке, заменяя слова английскими — речь от этого не станет английской. Это, скорее, просто разные способы мышления и подходы в постановке и обработке задач. Сложнее или проще для восприятия (лучше/хуже) — зависит во многом от опыта работы с конкретными языками и методик решения задач.
PS Как еще один вариант в абстрактной (не в LISP) нотации:
достать из печи тесто после того как оно
. простояло в течение 30 минут в
… разогретой до 175 градусов духовке
maxfox
16.06.2016 14:20+1Мне кажется, автор статьи в своей кулинарной аналогии не совсем честен. Императивная форма рецепта не полна… Интерфейс взаимодействия с духовкой? Размер противня? Какое масло? Какая мука? Как и чем взбивать? «А ну его к черту — я не могу это закончить!». Беда в том, что императивный рецепт может быть полезен лишь исполнителю-человеку, обладающему кулинарным опытом. При этом результат всегда будет разным в зависимости от исполнителя. Ну мы, конечно, покроем пирог автоматическими тестами, напишем полифилы для поваров-левшей и разработаем упрощенную версию для морально устаревших кондитеров. Мне кажется, что если подходить к разработке ответственно, то хрен редьки не слаще. Просто в случае императивного подхода часто забывают о том что работоспособность программы при любых входных данных неплохо бы доказать формально. А тут мы уже приходим к математической модели. Ну а если раз-два и в продакшен — то ФП, конечно, лишняя трата времени.
Ну и еще, как мне кажется, ФП не популярно не потому что оно странное (странных технологий немало), а потому что сильно отличается от того, подо что инфраструктура затачивалась десятилетиями. При наличии всесторонней поддержки — ОС, библиотеки, умные компиляторы, может быть даже железо — ФП может радикально повысить качество прикладного ПО.
guai
16.06.2016 15:18Тащемта, ФП-языки тоже сильно разные. Сгребли все их в кучу.
Внезапно, кложура с ее структурами данных позволяет хранить состояние не переставая быть что ни на есть функциональщиной.
Flammar
16.06.2016 15:57+2Понятия «программирование» и «алгоритм», на мой взгляд, уже в себе неявно подразумевают императивность и изменяемое состояние. По крайней мере, так сложилось. И на уровне машинных команд мы имеет те же императивность и изменяемые части состояния. Поэтому программирование без изменяемого состояния и должно казаться странным и требовать усилий для освоения.
kreslavsky
16.06.2016 17:28+2Оно, конечно, да. Однако, суть ФП не в программировании без эффектов, а в отделении эффектов от чистых вычислений. Они выполняют принципиально разные задачи, поэтому по отдельности с ними работать гораздо проще. Реюзать, тестировать, делать код ревью...
VolCh
16.06.2016 18:30Алгоритм, да, — по определению подразумевает императивность. Но вот изменяемое состояние — нет, в общем случае это деталь реализации исполнителя. А программирование не подразумевает даже императивности.
j3fe
16.06.2016 17:01Может, декларативное программирование без использования функций (не ФП), как базовой сущности может лучше проявить достоинства декларативных алгоритмов. Например, если брать vhdl, можно увидеть как декларативно описываются модули (узлы электрической схемы, вентили и регистры, входы/выходы), и всё описание системы представляет её слепок в один момент времени. Дальше мы добавляем исполнение всей схемы за один такт и когда такты следуют друг за другом, мы получаем работу тех или иных алгоритмов. А ещё все забывают про SQL, как пример декларативного языка. А через такие простые для понимания системы мы можем перейти к пониманию ФП.
VolCh
16.06.2016 18:33Никогда не понимал, почему SQL считают декларативным языком, если его выражения даже начинаются с глаголов в повелительном наклонение: выбери, обнови, вставь, удали, создай и т. д.
j3fe
16.06.2016 19:34+2Потому что это непошаговое описание содержимого результата. То есть, это ЧТО, а не КАК.
Но это не о хранимках, конечно.
Azoh
16.06.2016 17:14+2Пример с парсером неудачный. Выглядит это так:
1. Было у меня повторение кода. Плохо, плохо, нужно отрефакторить
2. Что-бы применить? М-м-м, мы же на плюсах пишем, шаблоны же
3. Какая боль, какая боль, шаблоны функциональные
4. Получилось костыльно
…
?.. Это все функциональщина проклятая
?+1. Я не против функциональных языков, но пускай там все будет как в императивных
При этом проблему можно было выразить куда проще. Шаблоны в C++ не достаточно удобны и не позволяют применить более мощные средства. Многие из которых, как ни странно, доступны в других функциональных языках.
IvanPanfilov
16.06.2016 18:05-3> Так уж оказалось, что шаблоны С++ — это функциональный язык
Афтар сделал мой день
> Функциональное программирование непопулярно, потому что оно странное
оно не странное. это чуваки которые пытаются чтото функциональное изобразить на не функциональных языках — странные.
хочется фцнкциональщины — пишите на хаскеле он вполне продашкен реди сколько лет, и не трахайте людям мозг своими ссаными шаблонами.VoidEx
16.06.2016 18:18+2>> Так уж оказалось, что шаблоны С++ — это функциональный язык
> Афтар сделал мой день
Ну, вообще-то именно так, чистое ФП. Напишите на шаблонах compile-time факториал.
0xd34df00d
17.06.2016 05:03На хаскеле, при всей моей бесконечной любви к нему, сложновато получить похожую на плюсы производительность. Я тут выше писал даже конкретный пример.
А ещё у вас аватарка такая… хорошая-хорошая!
thousandsofthem
16.06.2016 19:44+1Более менее реальный код, написанный на elixir — который вполне себе функциональный, с иммутабельными переменными т тэ пэ:
```
pan = pan
|> grease
|> flour
oven = oven
|> preheat(«175C»)
dough = mix(«flour», «baking soda», «salt»)
cake_mixture = mix(«butter», «white», «sugar»,«brown sugar»)
|> beat_until_fluffy
|> beat_mixed_with(«eggs»)
|> mixed_with(«bananas»)
|> mixed_with(:alternating, «buttermilk», dough)
|> mixed_with(:chopped, «walnuts»)
cake = cake_mixture
|> place_on(pan)
|> put_in(oven)
|> wait_for(«30min»)
|> eject
|> put_on(:towel)
|> wait_until_cool_down
```
Как видим, вся последовательность операций линейна и понятна. Может быть это «недостаточно функционально»? Пофиг, главное что удобно на практике.
P.S. |> это pipe operator, работает так:
a |> b |> c ==> c(b(a()))
77vlad
16.06.2016 21:04+2Те кто здесь рассуждает о функциональном подходе, то забывает о главном, что лямбда исчисление эквивалентно машине тьюринга. Таким образом, выразительная и вычислительная мощь императивных и функциональных языков ОДИНАКОВА!
Но есть ньюанс, никто не знает как должна выглядеть архитектура функционального фычислителя ввиду его полной абстракции ;)
Зато существует, уже полвека, неймановская архетиктура и все императивные языки являются надстройкой над этой архитектурой и максимально ее утилизируют. Все Функциональные языки являются надстройкой над виртуальной машиной, которая пишется в императивном стиле и использует реальную неймановскую архетикоуру.
Так что функциональные языки это попытка построить еще один уровень абстракции над императивным языком, а императивный язык это уровень абстракции над ассемблером и тд и тп
Вопрос риторический сколько уровней абстракции нужно для решения конкретной задачи. Как то такwarlock13
16.06.2016 22:47никто не знает как должна выглядеть архитектура функционального фычислителя ввиду его полной абстракции
"никто не знает как должна выглядеть" /= "не может существовать"
Все Функциональные языки являются надстройкой над виртуальной машиной
Это очень натянутое использование термина «виртуальная машина». Можно тогда и про императивные языки сказать то же самое.
Так что функциональные языки это попытка построить еще один уровень абстракции над императивным языком, а императивный язык это уровень абстракции над ассемблером
Поскольку ассемблер сам по себе является императивным языком, достаточно одного уровня абстракции — так же как и для императивных языков высокого уровня.77vlad
16.06.2016 23:42Абстракции ассемблера и алголоподобных языков сильно различаются, можно их считать за разные уровни
warlock13
17.06.2016 01:14Да, но вам не нужен алгол, чтобы сделать компилятор хаскелл — достаточно самого хаскелл и ассемблера.
77vlad
17.06.2016 07:39+2И что это принципиально меняет? Все равно используется неймановская архетиктура и императивный язык, а какие пляски с бубном начинаются при реализации много поточности и сборки мусора!
Так зачем, если не видно разницы, думать больше!? Сразу пишем на императивном языке ;)
Как правильно заметил автор, 95 процентов задач вполне решаются императивным подходом, плюс нароботана десяти-двадцатилетняя экспертиза по решению, плюс библиотеки
develop7
16.06.2016 22:49никто не знает как должна выглядеть архитектура функционального фычислителя ввиду его полной абстракции ;)
всё правильно, за исключением того, что таки знают как минимум один раз — https://ghc.haskell.org/trac/ghc/wiki/Commentary/Compiler/GeneratedCode77vlad
16.06.2016 23:36Вы привели пример реализации на неймановской архетиктуре и императивном языке. Я знаю еще несколько различных ИМПЕРАТИВНЫХ реализаций. Фактически функциональное программирование это синтаксический сахар над алголоподобными языками. Мое утверждение было гораздо более общим, архетиктура выч системы должна из коробки поддерживать функциональное программирование. Пока есть пример лисп-машины, но она тоже неймановская.
В реализации сила и слабость функционального языка, ибо с одной стороны язык не запрещает никаких способов реализации которые бы давали правильный ответ, но и никаким образом не подсказывает, какая реализация могла бы быть опимальной. Вся реализация лямбда исчисления и фп — математические концепции в голове разработчика.
Idot
17.06.2016 04:02никто не знает как должна выглядеть архитектура функционального фычислителя ввиду его полной абстракции ;)
А что не так с вариантом?
— берём функциональный код f(a,b)
— а затем преобразуем получая вот такое a b f
=> получился Forth! Который, по скорости вполне сравним с C => PROFIT!
Никто по поводу него критически так и не высказался.77vlad
17.06.2016 07:43Высказались уже лет тридцать назад и мало кто сейчас так пишет, а так fort хорош конечно, сам по себе.
Вы привели только функциональную часть синтаксиса, а есть еще и императивная которая все меняет
77vlad
17.06.2016 07:50Но самое большое бедствие функционального подхода это… Функции! И как и в имеративном подходе, каждый разработчик волен производить декомпозицию задачи на функции произвольным образом., что может приводить к очень большим проблемам при сопровождении и изменении программы.
То есть концептуально, для разработки сложных систем, ничего не поменялось, нет никакой страховки, что супер-пупер навороченная функция из десяти тысяч строк не содержит ошибки и не вынесет весь прекрасно выглядещий абстрактный и правильный вобщем код на крах системыnapa3um
17.06.2016 08:06+2Функция из десяти тысяч строк? Вам ни ФП, ни ООП не поможет, лучше заняться чем-нибудь другим, не программированием.
Idot
17.06.2016 10:03Legacy-Code — не вариант?
napa3um
17.06.2016 10:19Можно придумать множество оправданий самым нелепым явлениям. Но заниматься этим пользы мало.
Idot
17.06.2016 10:28+1Увы, вполне вариант. Бывает, что нужно искать ошибку в коде, написанном уже ушедшим сотрудником. И бывает, что цельный кусок в 5тыс. строк распутать и понять оказывается легче, чем лапшу в два-три десятка функций и процедур общим объёмом в тысячу строк. Тупо потому что все эти 5тыс. все — в одном месте, прямо перед глазами. А два-три десятка мелких функций и процедур приходится искать с матом по различным зависимостям. И да, legacy-code не обязательно старше десятка лет, ему вполне может быть два-три года, просто, он остался от человека уже покинувшего кампанию.
napa3um
17.06.2016 10:30+1У вас очень хорошая фантазия.
Idot
17.06.2016 10:46+1Нет, это Вы просто не получали legacy-code.
0xd34df00d
18.06.2016 00:38Я получал. Функции на пять тысяч строк распутывать таки на порядки труднее.
77vlad
17.06.2016 11:56Много раз сталкивался с такими функциями самый эпичный случай хранимая процедура на t-sql из 8 или 9 тыс строк и не одна, великий и могучий индусский код
warlock13
17.06.2016 11:16То есть концептуально, для разработки сложных систем, ничего не поменялось
Нет. Разница огромная: при функциональном подходе эта навороченная функция из 10000 строк чистая, и она имеет точно специфицированный вход и выход. Это резко упрощает работу с ней.
Например, вы можете сначала посмотреть на эту функцию как на чёрный ящик и написать на неё тесты (причём именно и только на эту функцию), после чего заняться сколь угодно «опасным» рефакторингом без риска что-нибудь сломать. Или выяснить, что функция работает неправильно только при определённом сочетании входных параметров и пофиксить это приписыванием затычки-обёртки, вообще не залезая в это болото, и при этом, в отличие от императивного программирования, есть хороший шанс, что это будет работать.77vlad
17.06.2016 12:12+3Наивный ;) где же это видано 10000 строк чистой функции, наоборот там будет чистый фарш из десятка монад, зависимостей и состояний, ну нельзя реальный мир на неймановской архитектуре описать чистыми функциями — хоть тресни.
А вообще, сколько раз (за двадцать лет программирования) я уже слышал о «новых прорывных» технологиях, которые сделают хорошо программисту, а воз и ныне там… уже не верю ни в серебряные пули, ни в эльфов из Микрософт, что выкуют новую ОС и язык программирования всеобщего счастья, как-то так.
Да и функциональное программирование живет себе уже 40 лет и уже десятое поколение начинающих программистов-фанатиков обещает всеобщее счастье и прогресс, реальность топчет все эти обещания кованным сапогом… в итоге имеем промышленный стандарт с++\c# и Java в которых что-то можно описывать в функциональной парадигме посреди императивного кодаwarlock13
18.06.2016 17:24+1наоборот там будет чистый фарш из десятка монад, зависимостей и состояний
Вот в том и сила функционального подхода, что внутри функции может быть фарш из монад, зависимостей и состояний, а только если функция чистая — значит она чистая и неважно что внутри.
akamensky
17.06.2016 12:24-1На самом деле ООП это абстракция над функциями. Процессор не знает ничего про ООП и на стадии компиляции все объекты и их методы преобразуются в набор функций (на самом деле даже не функций а под-программ с набором условных или безусловных переходов).
И мне кажется непопулярность ФП это в некотором роде выдумка. Если писать огромную систему документооборота (например), то в этом случае без ООП не обойтись. А если писать например утилиту для системного администрирования, которая уместиться в один файл, то ООП это overkill.
ООП и ФП это просто инструменты которые нужно правильно применять. И право на существование имеют оба подхода.VolCh
17.06.2016 13:19+2ООП — усовершествнование процедурного императивного программирования, а не функционального. Именно «на самом деле даже не функций а под-программ с набором условных или безусловных переходов» и данных, которые лежат в памяти и их эти подпрограммы меняют.
vlad72
17.06.2016 12:25А ну покритикуйте…
Значит, вначале был императивный подход с такими особенностями:
— однопоточность,
— отсутствие хранимых состояний,
— чистые функции.
Потом появилась необходимость сохранять состояния (чтобы вызывать их и обрабатывать в любой момент) — появилось ООП со следующими особенностями:
— частичная многопоточность,
— сохраняемые состояния,
— отсутствие доступа к чистым функциям (которые оказались инкасулированны внутри объектов, «загрязненных» состояниями).
Потом стало нехватать многопоточности и вспомнили про ФП, где есть:
— доступ к чистым функциям (конечно, функции сами по себе всегда чисты, но так уж повелось),
— неограниченная многопоточность,
— отсутствие состояний.
Можно конечно «в лоб» объединить ООП и ФП в одном языке, но почему бы не поступить так:
— есть чистые функции (как в ФП),
— есть недо-объекты (в которых хранятся только данные), назовем их Н-объекты (они являются только носителями состояний),
— Н-объекты вызывают чистые функции, которые передают выходные данные в: вызывающий Н-объект; другой Н-объект; другую функцию.
Получаем два множества сущностей, в одном — Н-объекты, в другом — чистые функции. Можно переходить к графическому программированию, где отношения между ними будут задаваться в виде схемы (что будет очень наглядно, быстро и надежно).77vlad
17.06.2016 13:08+2Ага! Гладко было на бумаге… как только заговорили о многопоточности решили все разделить по функциям, а потом вспомнили о синхронизации, а как ее осуществлять из чистых то функций!? Опять неймановская архетиктура стала из под абстракций торчать
ООП и ФП вообще разные вещи и никак друг-другу не противоречат.
77vlad
17.06.2016 13:36+1А кстати, давайте снимем последние покровы!!! Нахрен
Чистота функций в ФП млять… Какая нах… чистота функций может быть, если все реализуется на императивной неймановском процессоре!? Какая, я вас спрашиваю!?
Если вы в коде на haskel не написали, var a = 100001 или там a+=500, это еще не значит, что ваш код пахнет ромашками, а у соседа пишущего на c++, пардон, г… м.
А стеки вызовов функций? А указатели на голову и хвосты? А стеки данных и буферы потоков ввода/вывода? Вы думаете этого всего нет в haskel!? Тогда купите себе билет в дурдом и больше не пишите го… код!
Все что привносит современное ФП, запиленное поверх императивных языков, под маркой «чистоты» это то, что с проблемами состояния, типизации, выделения памяти борется кто-то другой, а не ФП программист.
В реальности, под капотом реализации любого ФП языка живет гипертрофированное внутренне состояние вычислительной среды, имеющее тенденцию распухать и множится. И уже не понятно, что есть меньшее зло, честно сказать компилятору, чтобы он выделил десять байт под массив и заносил туда данные простым присваиванием или это будет делать невидимый монстр который на каждый чих в рекурсивном цикле будет: выделять десять байт; копировать состояние из предыдущих десяти байт; менять один из байтов и так сотни и тысячи раз! А затем в дело вступает сборщик мусора, который останавливает нашу программу проверяет все эти созданные массивы решает, что ненужно чистит и упаковывает память. Так что сразу прощай и память и производительность.
И любой разработчик, не зная всех особенностей реализации, может безобидной функцией на ФП языке завалить всю систему в глубокий дос. Плавали — знаем, на C# такой сценарий как два пальца, а если быдлокодеру дать еще и haskel с clojure — туши светnapa3um
17.06.2016 14:07+4Чистота функций в ФП математическая, в голове программиста, проектирующего на нём информационную систему и контролирующего сложность проекта, а не в исполняющей среде. Чистота нужна не для того, чтобы убеждать себя в чистоте исполняющей среды, а как раз для того, чтобы декомпозировать систему на максимально независимые блоки в условиях «грязного» (физического) исполнителя. Т.е., для некоторых гарантий относительно того, куда грязь не затечёт. Все эти ООП, ФП, КОП, АОП, КПСС — лишь способ отыскать наиболее удачные «линии разреза» системы для разложения на малозависимые и удобно поддерживаемые блоки кода, а не для того, чтобы устраивать религиозные войны на тему выбора единственного божественного пути. Вы воюете не с ФП, а с чучелом, которое сами придумали.
77vlad
17.06.2016 14:26-1Вот именно, чистота то математическая, а разработка идет на реальной платформе имеющей свои ограничения. Поэтому ФП — абстрактно здорово, а реально только в отдельных случаях хорошо.
napa3um
17.06.2016 14:39+5Вы из тех, кто программирует на процессоре, а не на ЯП? Да, ФП — это именно абстрактно и здорово, и, конечно, оно нужно далеко не всем и не всегда, а только тогда, когда такая абстракция уменьшает совокупную сложность. Это именно вы видите лишь две крайности — либо всё ФП, либо всё ООП и никак иначе. Представляю, каким было ваше возмущение о существовании сортировки пузырьком, когда вы узнали о сортировке слиянием.
Saffron
Головоломки — не самое страшное, к ним можно притерпеться. Я достаточно быстро обнаружил, что не могу собрать сложный проект из маленьких частей на хаскеле. ООП даёт средства для декомпозиции, а хаскель — нет. Ну и проблемы с производительностью. Все виденные мною реальные применения хаскеля приходили к тому, что используется прямой доступ к памяти, глобальное состояние и строгие алгоритмы. Хаскель давит своей идеологией абсолютной чистоты, но чистота эта встречается лишь в мелочах.
VoidEx
Хаскель даёт средства декомпозиции — функции
mad_celt
Еще бы. Хаскель — академический язык, который хорошо и приятно используется в той, и только той, области, для которой был создан — в теории категорий.
Хаскель, будучи языком потрясающе аккуратным и красивым языком, при этом совершенно не подходит для задач, реализуемых не докторами наук. К сожалению, простые задачи на нем очень быстро набирают высокую сложность и требуют ощутимо больших трудозатрат, чем практически все другие функциональные языки, и дело здесь совершенно не в наличии или отсутствии библиотек, а в необходимости тщательного проектирования взаимодействия сайдэффектов и чистого кода, что хорошо подходит для академического программирования и совершенно не работает с промышленным.
graninas
Не могут же быть столь категоричные заявления правдой? Как и любые категоричные заявления.
Хаскель подходит для промышленных задач, и люди, которые его используют именно так, очень недоумевают по поводу мифов, в том числе и приведенных вами.
eugzol
> Не могут же быть столь категоричные заявления правдой? Как и любые категоричные заявления.
Включая данное. Пардон, не удержался :)
kmikeru
Это сейчас серьёзно было?
www.haskell.org/onlinereport/preface-jfp.html
The committee's primary goal was to design a language that satisfied these constraints:
It should be suitable for teaching, research, and applications, including building large systems.
ganqqwerty
ну так это они описывают то, что хотели, а не то, что получилось
kmikeru
Читаем ещё раз тезис «той области, для которой был создан — в теории категорий». По ссылке сказано, что это, мягко говоря, не так.
ganqqwerty
Ваша правда, может они и создавали его не только для академических проектов. Но гляньте на список авторов — там же сплошная академия, всего один человек от индустрии. Академики пишут академические проекты, говорю как участник одного из них.
potan
А в чем проблема со сборкой?
Для немонадических «маленьких частей» все вообще очевидно. Композиция функций с возможным добавлением прослоек с преобразованиями типов.
С монадическими или стрелочными «частями» надо смотреть. Если монады не покидают границ модуля, как в первом случае. Если это простая монада, которую можно «запустить» (типа парсера), тоже обычно все просто. Если там IO или STM, то применять их можно только в другом монадическом коде — но это получается мало отличается от обычного императивного программирования.
potan
С производительностью проблемы могут возникнуть из-за ленивости — долго работающие программы могут накапливать в памяти недовычисленные данные.
Искать эту проблему бывает не просто, но возникает она когда все остальное уже работает.
Flammar
И отлаживать ленивость труднее…
potan
Мне очень мало приходилось отлаживать программы Haskell — если они компилировались, почти всегда работали.
А вот привычка к ленивости при переходе на C++ и Scala принесла необходимость отлаживать инициализацию взаимозависимых объектов. В ленивом языке с этим было проще.
0xd34df00d
Отлаживать как раз проще, с таким-то repl'ом.
Я тут переписывал прототип с хаскеля на плюсы — очень страдал по этому поводу.
0xd34df00d
У меня был код, который загружал архив новостей, делал токенизацию, некоторые преобразования (от HTML-тегов почистить там, это всё), дёргал NLP.Snowball для стемминга, а потом полученное интернировал (заменял токены на их уникальные Int32-идентификаторы, дабы памяти меньше жралось и вообще SVM потом на это дело натравить можно было, например). Ну, да и всё, я хотел просто посмотреть, что из этого получится, и дальше плясать от данных.
В общем, работало оно, скажем, 60 минут на тестовом датасете.
Я попрофилировал, увидел, что gc дохрена жрёт. Поковырял heap profile, -hc, это всё, повставлял строгости где надо, где надо вообще
force
сделал, стало жраться 20 минут.Я подумал, что это много, попрофилировал, увидел, что много времени жрётся в интернировании в чисто функциональной Data.HashMap.Strict. Ну я ж не лыком шит, у меня есть Data.HashTable, живущее в ST, поэтому я хорошенько обдолбался и переписал этот код так, чтобы оно всё завернулось в ST (на самом деле в произвольную монаду m, Identity для старого Data.HashMap и ST s для нового Data.Hashtable), заодно ментально поонанировал на
{-# LANGUAGE FunctionalDependencies #-}
(ну серьёзно, чувствую себя прям на острие прогресса, когда пишу конкретно эту прагму), а то иначе тайпчекер не был доволен связью m с остальным кодом, и оно стало работать, ну, скажем, 9 минут.9 минут — это тоже много, поэтому я попрофилировал ещё, увидел, что 60% времени жрётся в стемминге, остальное размазано ровным слоем, и погрустнел, потому что реальные объёмы данных планировались на пару порядков больше.
Потом через пару недель у меня дошли руки переписать всё на плюсах. Ну, в общем, на плюсах оно 20 секунд работает. Включая стемминг в том же snowball. Хотя байтики ручками в std::string двигать приходится, конечно, это да, да и писать на boost.spirit больнее, чем на attoparsec.