Здравствуйте, AVC, Вы писали:
AVC>Мы хотим ее поменять (прямо или косвенно переприсвоив значение fabrique), чтобы обращение к fabrique из произвольных мест программы приводило к созданию объектов иного конкретного типа, отличного от типа по умолчанию. AVC>Как это делается в ФП?
В данном случае (в твоем примере) — это работа с глобальной переменной. Помимо подхода "найти функцию fabrique по имени", есть ещё возможность не использовать глобальную переменную, а передавать переменную в контексте, либо получать из глобальной переменной (примеров туча — вот под рукой JNDI, например). С передачей переменной в контексте вызова, наверное, всё ясно и тут нечего обсуждать, а вот использование глобальной переменной в чистом языке можно только с помощью хаков: в Haskell это, например, unsafePerformIO, либо ImplicitParameters. Так что для совсем-совсем чистого языка остается только передача в контексте имхо. Параметром, например, или вшить в монаду
Я вижу, что все пытаются объяснить мне одну и ту же вещь (или, по крайней мере, это очень похожие вещи ).
Примерно то же пытаются объяснить мне и geniepro, и др. функциональщики.
Ребята, я очень ценю ваши усилия, и очень вам благодарен.
Но то ли сказывается влияние стресса (завтра у меня сдача программы), то ли еще что-то, но я до конца "не въезжаю".
А может быть, мы вплотную подобрались к тому самому "слепому пятну", который мешает одному конкретному императивщику (мне ) понять ФП.
Да, я вижу, что в одном случае (StatefulMachine) мы используем многократное присваивание, а в другом (StatelessMachine) — однократное.
Я вижу, что императивную программу можно исполнить как бы функциональным способом.
Т.е. сам "трюк", его технику я понимаю.
Но здесь есть какая-то загвоздка для моего сознания.
Во-первых, требуется много копий StatelessMachine, чтобы исполнить императивную программу.
Причем не совсем ясно, для чего такие ухищрения, ведь "новая" машина полностью заслоняет собой "старую", и, следовательно, "старая" машина уже не может быть использована.
Во-вторых, множественные StatelessMachine исполняются все же на одной-единственной (в данном случае) машине с состояниями.
Это оставляет ощущение какого-то "надувательства", что-то вроде того, что если машины без состояния "расползутся", то "запечатать" их можно только в машине с состояниями большего размера.
А главное, в третьих, возникает впечатление (возможно, ошибочное), что машину без состояния надо каждый раз порождать целиком.
А если там будет не один регистр?
Все это, конечно, могут быть просто мои глупости.
Возможно, завтра, уже без стресса, посмотрю спокойно и все пойму.
Но существует одно качество, которое нельзя купить, — это надежность. Цена надежности — погоня за крайней простотой. Это цена, которую очень богатому труднее всего заплатить.
Здравствуйте, geniepro, Вы писали:
A>> В "обыкновенном" императивном программировании (в ООП особенно) именно A>> побочные эффекты зачастую являются источником значительной гибкости.
G>Оператор GOTO тоже даёт значительную гибкость, вот только Вы от него почему-то отказались вслед за Виртом и Хоаром...
Здесь, IMHO, есть одна важная тонкость.
Действительно, от оператора goto мы отказались (и об этом не жалеем).
Но это не значит, что мы отказались от переходов вообще.
Мы просто следуем определенной дисциплине.
В конце концов, разве IF ... THEN ... ELSIF ... END, WHILE ... DO ... END и т.д. не включают в себя переходы?
Но только подчиненные дисциплине структурного программирования.
Но существует одно качество, которое нельзя купить, — это надежность. Цена надежности — погоня за крайней простотой. Это цена, которую очень богатому труднее всего заплатить.
Здравствуйте, AVC, Вы писали:
AVC>Здесь, IMHO, есть одна важная тонкость. AVC>Действительно, от оператора goto мы отказались (и об этом не жалеем). AVC>Но это не значит, что мы отказались от переходов вообще. AVC>Мы просто следуем определенной дисциплине.
Золотые слова! Смотри: goto плох, потому что превращает программу в вермишель, запутывая логику. Мы вынужденны параллельно поддерживать инфраструктуру меток или нумерацию строк программы.
Переменные, в каком-то смысле, те же метки, и их избыток тоже ведёт к запутыванию логики. В ИЯ мы вводим их не задумываясь, неоднократно меняем их значение; реализуя сложный алгоритм, мы вынуждены держать в голове все пути к данной точке программы, для того чтобы проиграв в голове все допустимые изменения переменных, осуществить требуемые в этой точке проверки. В Хаскелле "переменные" просто сокращают запись:
let x = expr1 in expr2
Выражение expr1 может быть весьма сложным и тип его тоже может быть далёким от тривиального (например, это может быть функция). Мы вводим такие "переменные", когда обнаруживаем, что в выражении expr2 одно и то же подвыражение (expr1) встретилось несколько раз.
В ИЯ мы строим программу снизу вверх: соединяем детальки конструктора (переменные) в большом количестве, в надежде, что получившаяся конструкция будет работать как надо. В ФЯ мы программируем сверху вниз: берём задачу и начинаем нарезать её на логические части, пытаясь свести её к известным комбинаторам. И тот и другой подход непрост, и каждый требует специфических сноровок. (этот абзац написан в контексте оператора ИМХО )
Здравствуйте, lomeo, Вы писали:
L>Можно чуть больше конкретики? Каких именно вещей не рассказывают (т.е. может быть они что то должны рассказать перед чем то, а они этого не делают)? Плохо подают материал — как надо хорошо?
Добрый день, товарищи!
Считайте это пост воплем души ТУПОГО императивщика.
Периодически читаю топики подобные этому и статьи разных популяризаторов ФП. И у меня
все время возникает вопрос а как это можно реально применить? Примеры a->b->c->d->....->z
уже сидят в печенках. А слабо заменить это реальными goods,sales,employers и.т.д. И показать
преимущества ФП на РЕАЛЬНЫХ(многопотчных, с гуеей, с БД, с удаленными вызовами) задачах. А самое главное как можно выполнить декомпозицию такой реальной задачи (НЕ МАТЕМАТИЧЕСКОЙ!!!!) в терминах, абстракциях и паттернах ФП. И наглядно показать где здесь преимущества.
Проблемы всех этих статей в том что они сразу начинают грузить читателя терминологией
(а в худшем случае вышкой). Мне лично кажется что танцевать нужно от конкретной задачи.
По поводу терминиологии в ФП это еще одна проблемма, такого обилия терминов (которые имеет математические корни и поэтому слабо соотносятся с реальностью) при описании задачи в терминах ИП+ООП нет. В минимальном случае достаточно нарисовать диаграмму классов(понятий) и описать последовательность действий(в тех или иных случаях) своими словами (список прецедентов).
У меня как у ТУПОГО императивщика есть предчувствие что в процессе реализации такой задачи на ФЯ
Вы упретесь в эти самые побочные эффекты (посмотрел я тут на цирк с состояниями...).
ИЯ+ООП предлагают простой и понятный способ декомпозции задачи на:
1. Ряд последовательных шагов, объединенных в методы, которые вызывают другие методы.
2. Иерархию сущностей, хранящих данных + ассоциированные с ними методы.
ИЯ+процедуры тоже вроде бы все понятно, по крайней мере не возникает вопросов с порядком действий и хранением состояний.
Все просто понятно и замечатльно, и соответствует тому что мы видим в окружающем мире (та же
последовательнть действий+иерархия сущностей(понятий)).
ФП насколько я понял, предлагает использовать функции, которые вызывают другие функций и принимают в качестве аргументов третью (или данные), но чем это отличается от методов вызывающих другие методы?. А как организованы данные? А что делать с вещами, состояние которых в принципе надо хранить (сессии, соединения)? А как в терминах ФП разложить конкретную реальную задачу?
Вот они вопросы на которые ФП-шники почему-то не дают конкретных ответов, а вместо этого начинаются лекции про монады и т.п.. Потом прибегают другие ФП-шники и они благополучно начинают
общаться между собой.
Про книги и их авторов я вообще молчу — никакой обратной связи.
P.S. Уважаемые функциональщики я никого из Вас не хочу обидеть той глупостью которую я здесь
возможно написал, но хотелось бы увидеть реальные и конкретные примеры из которых видно
преимущество ФЯ над ИЯ (вышку не предлагать!!!).
P.P.S. Очень хочется разобраться в ФП с целью расширения кругозора (хотя его реального применения
в качестве альтернативы ИП я не вижу)...
__> Периодически читаю топики подобные этому и статьи разных популяризаторов ФП. И у меня __>все время возникает вопрос а как это можно реально применить? Примеры a->b->c->d->....->z __>уже сидят в печенках. А слабо заменить это реальными goods,sales,employers и.т.д. И показать __>преимущества ФП на РЕАЛЬНЫХ(многопотчных, с гуеей, с БД, с удаленными вызовами) задачах. А самое главное как можно выполнить декомпозицию такой реальной задачи (НЕ МАТЕМАТИЧЕСКОЙ!!!!) в терминах, абстракциях и паттернах ФП. И наглядно показать где здесь преимущества.
я попытался донести функциональные вещи в наиболее поплярном виде (ну, насколько смог). К сожалению, она пока доступна только в бумажной версии RSDN Magazine, но через некоторое время появися и на сайте.
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, mini_root_2, Вы писали:
__>Считайте это пост воплем души ТУПОГО императивщика. __> Периодически читаю топики подобные этому и статьи разных популяризаторов ФП. И у меня __>все время возникает вопрос а как это можно реально применить? Примеры a->b->c->d->....->z __>уже сидят в печенках. А слабо заменить это реальными goods,sales,employers и.т.д. И показать __>преимущества ФП на РЕАЛЬНЫХ(многопотчных, с гуеей, с БД, с удаленными вызовами) задачах. А самое главное как можно выполнить декомпозицию такой реальной задачи (НЕ МАТЕМАТИЧЕСКОЙ!!!!) в терминах, абстракциях и паттернах ФП. И наглядно показать где здесь преимущества.
Я тоже так переживал, пока не понял: путь в функциональный стиль лежит через языки, которые обеспечивают возможность
использование обеих подходов.
Перед тем как я это понял, я тоже пытался использовать ФЯ. Начал с erlang, как наиболее простого
(как мне показалось) и с достаточно вменяемым синтаксисом (с т.з. императивщика).
Но разбираясь с этим языком и пытаясь написать простенький проект, столкнулся с кучей сложностей, подобных
тем, которые задавались выше... В принципе многим из них находятся решения (и часто очень красивые), но
низкая производительность такого подхода, когда на каждую мелочь, о которой не задумывался в ИЯ, тут
приходилось перелопачивать тонну литературы, т.к. мозги отказывались придумать что-то внятное, начал думать,
что ФЯ не для меня.
Но большинство идей ФЯ (матчинг, функции высшего порядка, guards, tail recursion, макросы) как наркотик,
единожды попробывал, и уже в обычные языки выглядят убогими и очень многословными. И тогда обратил свой
взгляд на смешанные языки, в моем случае это Scala.
Использование таких языков позволяет не зацикливаться на решении какой-то проблемы в функциональном
стиле, а просто написать ее в императивном и по мере необходимости (или нахождения решения для
функционального стиля) это можно отрефакторить.
Пример из Scala By Example:
Т.е. сначала пишем императивно:
def sort(xs: Array[int]) {
def swap(i: int, j: int) {
val t = xs(i); xs(i) = xs(j); xs(j) = t
}
def sort1(l: int, r: int) {
val pivot = xs((l + r) / 2)
var i = l; var j = r
while (i <= j) {
while (xs(i) < pivot) { i = i + 1 }
while (xs(j) > pivot) { j = j - 1 }
if (i <= j) {
swap(i, j)
i = i + 1
j = j 1
}
}
if (l < j) sort1(l, j)
if (j < r) sort1(i, r)
}
sort1(0, xs.length 1)
}
Потом легко переделываем на функциональный (правда тут изменилась семантика,
т.к. вернется копия массива, в отличии от императивной, которая работает
inplace, но это не важно, важно видеть, что один и тот же язык позволяет
использовать оба подхода):
Где здесь нарушение логики? Идея, которую я уже несколько раз встречал: сделать в Enterprise всю высокоуровневую склейку разнородных компонент на каком-либо функциональном языке. С одной стороны ФЯ для этого достаточно гибкие, с другой — идея функций как чёрных ящиков и отсутствие изменяемых состояний позволяет легко абстрагироваться от внутренней машинерии склеиваемых компонентов.
Ещё в ту же копилку:
Вроде где-то уже пробегала ссылка на статью Эрика Мейера.
Abstract
Programmers in the real world wrestle every day to overcome
the impedance mismatch between relational data, objects, and
XML. For the past ten years we have been working on solving
this problem by applying principles from functional programming,
in particular monads and comprehensions. By viewing
data as monads and formulating queries as comprehensions,
it becomes possible to unify the three data models and their
corresponding programming languages instead of considering
each as a separate special case.
To bring these theoretical ideas within the reach of mainstream
programmers, we have worked tirelessly on transferring
functional programming technology from pure Haskell, via Cw
to the upcoming versions of C# 3.0 and Visual Basic 9 and the
LINQ framework. Functional programming has finally reached
the masses, except that it is called Visual Basic instead of Lisp,
ML, or Haskell!
Здравствуйте, Andrei N.Sobchuck, Вы писали:
ANS>Здравствуйте, deniok, Вы писали:
D>>Где здесь нарушение логики?
ANS>В том, что один ФЯ меняют на другой.
Ну они там какие-то выигрыши декларируют. Столбики чем правее, тем ниже
Здравствуйте, deniok, Вы писали:
D>Здравствуйте, Andrei N.Sobchuck, Вы писали:
ANS>>Прикольно. В качестве выгоды от функциональных языков приводится пример с заменой XSL на ML. Мне это кажется... э-э... алогичным.
D>Где здесь нарушение логики? Идея, которую я уже несколько раз встречал: сделать в Enterprise всю высокоуровневую склейку разнородных компонент на каком-либо функциональном языке. С одной стороны ФЯ для этого достаточно гибкие, с другой — идея функций как чёрных ящиков и отсутствие изменяемых состояний позволяет легко абстрагироваться от внутренней машинерии склеиваемых компонентов.
Все привыкли, что Enterprise — это такая многокомпонентная штука, позволяющая подключать необходимое число
независимых компонентов даже на разных машинах (они сами картинки банков приводят), и как итог предлагается
отказаться от такой многокомпонентности (они в выводах пишуть что как де хорошо, что все в одной vm вертиться)
и сделать по сути standalone приложение... Но вопрос, а когда нехватит производительности одного компутера,
тогда получается будет выбор: растить дальше (что не всегда возможно) или разбивать на куски. Вот тут то
и вылезет то, что нагородили в ентерпрайзе за годы его существования. Да и замена xsl на ml — суть замена
одного фя на другой. Но только еще предлагается вместо использования кубиков захардкодить это все
на ml-е... Но если захардкодить логику в программе с которой они сравнивали, думаю производительность
уже не будет так сильно разниться...
По этому и возник вопрос, к чему они все это написали? И у имепративщиков (а многие приходят с большим
бакграундом java или DCOM + Базы данных) и возникают вопросы, как те решения, что они премняли раньше
решать в ФЯ. И ответов действительно нет. Хотя части из функциональщины действительно недурно помогают.
Но полностью отказаться от императива в пользу ФЯ — думаю это не совсем разумно (по факту весь придыдущий
опыт идет на свалку) ...
ЗЫ: на правах оффтопика
Все таки мир у нас императивен. Когда нам надо сесть в машину мы выполняем метод "открыть" над объектом
"машина", а не делаем копию машины рядом, но уже с открытой дверью...
A>ЗЫ: на правах оффтопика A>Все таки мир у нас императивен.
Мир пассивен. Это воля императивна. А разум — декларативен.
A>Когда нам надо сесть в машину мы выполняем метод "открыть" над объектом A>"машина", а не делаем копию машины рядом, но уже с открытой дверью...
А мы пишем функцию
открыть :: нечтоСРучкой a => a -> a
открыть = потянутьНаСебя . ручкуВверх
и пускай компилятор думает, сколько и чего там нужно копировать, и нужно ли вообще
Здравствуйте, deniok, Вы писали:
D>Здравствуйте, aka50, Вы писали:
A>>ЗЫ: на правах оффтопика A>>Все таки мир у нас императивен.
D>Мир пассивен. Это воля императивна. А разум — декларативен.
+1 (это твое? или кто из древних? )
A>>Когда нам надо сесть в машину мы выполняем метод "открыть" над объектом A>>"машина", а не делаем копию машины рядом, но уже с открытой дверью...
D>А мы пишем функцию D>
D>открыть :: нечтоСРучкой a => a -> a
D>открыть = потянутьНаСебя . ручкуВверх
D>
D>и пускай компилятор думает, сколько и чего там нужно копировать, и нужно ли вообще
Но ведь он может и скопировать... хотя если рассмотреть открывание ручки на прямой
времени, то конечно получается копия (машина то в момент времени t1 так и осталась
с закрытой дверью , а в t2 — уже с открытой)
ЗЫ: а вообще очередной раз убеждаюсь, что мозги >10 лет работавшие с императивом
нереально сложно переделать на фстиль... (для меня во всяком случае)... остается
только идти в обход
Здравствуйте, aka50, Вы писали:
A>>>Все таки мир у нас императивен.
D>>Мир пассивен. Это воля императивна. А разум — декларативен. A>+1 (это твое? или кто из древних? )
Писал сам. Думаю, что в одно ухо мне шептал дух Ницше, а в другое — Шопенгауэра
Здравствуйте, aka50, Вы писали:
A>Потом легко переделываем на функциональный (правда тут изменилась семантика, A>т.к. вернется копия массива, в отличии от императивной, которая работает A>inplace, но это не важно, важно видеть, что один и тот же язык позволяет A>использовать оба подхода):
A>
A> def sort(xs: Array[int]): Array[int] =
A> if (xs.length <= 1) xs
A> else {
A> val pivot = xs(xs.length / 2)
A> Array.concat(
A> sort(xs filter (pivot >)),
A> xs filter (pivot ==),
A> sort(xs filter (pivot <)))
A> }
A>
A>Так что может и не надо сразу лезть в пекло, а можно войти в этот мир постепенно
Но в чем, всеже, прелесть Функционального Программирования???
А пример с сортировкой, который приводится (во многих статьях о ФП) в качестве превосходства над ИП, наверно, не удачен.
Это просто другая реализация алгоритма, которую можно легко использовать и в ИП (может не во всех, конечно).
Например Perl:
sub qsort
{
my $x = shift or return;
my @sx = @_;
return qsort( grep { $_<$x } @sx ), $x, qsort( grep { $_>=$x } @sx );
}
NK>Но в чем, всеже, прелесть Функционального Программирования???
NK>А пример с сортировкой, который приводится (во многих статьях о ФП) в качестве превосходства над ИП, наверно, не удачен. NK>Это просто другая реализация алгоритма, которую можно легко использовать и в ИП (может не во всех, конечно). NK>Например Perl:
NK>
Вообще, все священные войны ФП vs. ИП от лукавого. В ИП мы пляшем от переменных (объектов) и думаем над тем, как бы их преобразовать так, чтобы получить требуемый результат. В ФП мы пляшем от функций и думаем над тем, как бы их скомбинировать так, чтобы получить требуемый результат. Эти подходы можно совмещать, тогда появляются смешанные языки (которых на самом деле большинство). Однако некоторые языки более ориентированны на один подход, а другие на другой. Ориентированны в том смысле, что их синтаксические конструкции подталкивают нас к тому, чтобы решать задачи либо в терминах модификации переменных, либо в терминах комбинирования функций.
Скажем, характерная для целой группы ФЯ система типов Хиндли-Милнера подталкивает нас к тому, чтобы писать все функции максимально обобщёнными. (Фактически для этого и делать ничего не надо , само получается). Или, в качестве другого примера — рекурсия, как один из основных инструментов работы. Рекурсивные типы данных прячут рекурсию внутрь определения типа, а сопоставление с образцом позволяет подобного рода типы разбирать.
То есть дизайн ФЯ направлен на удобство программирования функциями.
А так, большинство языков полны по Тьюрингу, и то что на одном написано, может быть на другом перевыражено.
AVC wrote:
> Во-первых, требуется много копий StatelessMachine, чтобы исполнить > императивную программу.
Заблуждение. Здесь описывается только что делать, а не как делать. Умный компилятор может просечь, что старая машина
нигде не используется и записать состояние новой в то же место "физической памяти".
По сути такая оптимизация даёт представление о сути оптимизации хвостовой рекурсии.
> Причем не совсем ясно, для чего такие ухищрения, ведь "новая" машина > полностью заслоняет собой "старую", и, следовательно, "старая" машина > уже не может быть использована.
В данном примере заслоняет. В принципе — не заслоняет. Можно хранить ссылки на промежуточные машины. Получается, что вся
история процесса изменения состояния — односвязный список значений. Последовательно идущие элементы списка могут
"схлопываться" для оптимизации, если они больше нигде не используются.
Это даёт очень интересный результат — при отладке "правильной" (чистой) функциональной программы можно не только делать
"step forward", но и "step backward"!
> Во-вторых, множественные StatelessMachine исполняются все же на > одной-единственной (в данном случае) машине с состояниями.
Ты имеешь в виду машину тьюринга (или CPU)?
> Это оставляет ощущение какого-то "надувательства", что-то вроде того, > что если машины без состояния "расползутся", то "запечатать" их можно > только в машине с состояниями большего размера.
Тоже не понял.
> А главное, в третьих, возникает впечатление (возможно, ошибочное), что > машину без состояния надо каждый раз порождать целиком. > А если там будет не один регистр?
Будут порождаться только изменённые регистры. Короче, это уже больше относится к оптимизации. Главное — сама идея такой
структуры вычислений
> Все это, конечно, могут быть просто мои глупости. > Возможно, завтра, уже без стресса, посмотрю спокойно и все пойму.
Posted via RSDN NNTP Server 2.1 beta
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай