Здравствуйте, Quintanar, Вы писали:
Q>Самый главный аргумент против — это то, что при интенсивном применении монад код становится императивным и по сути и по виду. Это не может не бесить.
Я не знаю почему тебя это бесит. Меня лично раздражает, то что люди выдумывают средство эмуляции действительности. Хотя что ее эмулировать?
Казалось бы. Пометь блок кода и скажи, что он должен выполняться последовательно и не лениво. И все! Пиши себе в других местах пользуясь всеми приемуществами линивости, а когда надо просто переключайся в императив. Но нет, мы создаем себе сказку и усилиенно пытаемся поверить в нее. В итоге получаем тоже самое, но сложно, запутано и медленно.
В том же C# ленивость включается итераторами. Не так круто конечно как в Хаскеле, но блин, большинство проблем решается красиво и эффективно! А если бы можно было вот так запросто пометить код как ленивый, то было бы вообще супер.
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, EvilChild, Вы писали:
Q>>Самый главный аргумент против — это то, что при интенсивном применении монад код становится императивным и по сути и по виду. Это не может не бесить. EC>Почему по сути?
Потому что монады — это и есть императивыне участки кода, только их очень сильно пытаются замаскировать под офигительно умную идею.
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, EvilChild, Вы писали:
EC>А какие им есть альтернативы в случае чистого ФЯ? Кроме Uniqueness Typing из Clean?
На самом деле есть простое и доступное понятие — левивость вычисления.
Как не смешно к чистоте функцинальности оно имеет очень попосредованное отношение. Это деталь реализации. Просто чистый функциональный код куда проще сделать ленивым. Но итераторы C# и континуэшоны Руби отлично показыают, что код может быть ленивым и в императивных языках.
Так вот если код не линивый, то у него есть два важных свойства:
1. Он выполняется четко последовательно.
2. Вычисления не могут "подвиснуть".
Таким образом достаточно сказать, что код не ленивый, и будь он функциоанльным, или не фукнциональным проблем с общением с "внешним миром" возникнуть не может.
Так вот ленивость можно рассматривать как атрибут участка кода. Опять же берем в пример C#-вые итераторы. Функции реализвющие итератор можно считать ленивыми. Их значение отложено. Мы можем написать:
var res = SuperPuperQuary();
но вычисление не произойдет. Оно будет отложено до фактического извлечения результатов.
Можно создать язык (или зименить имеющийся) в котором будет можно просто помечать код неким атрибутом ленивости. Ну, а компилятор в зависимости от этого атрибута будет генерировать ленивый или не ленивый код для соотвествующих участков. Задача эта сложная, но мне кажется, решаемае. В результате мы получим гибридный язык в основе которого лежит простая и понятная идеология, а не зауности.
Кстати, в реальной жизин я бы предпочел иметь язык в котором по умолчанию исполнение было бы не ленивым, но его можно было бы включить пометив неким образом функцию или блок кода. Плюс можно рассматривать ленивыми все генераторы (конструкции вроде [1, 3, ... ]) и возможно что-то еще.
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, BulatZiganshin, Вы писали:
BZ>>>но ведь именно на это соображение я выше и ответил! представь себе скажем имя лог-файла, которое нужно передавать во все процедуры, поскольку запись в лог-файл может понадобиться где-то в самом низу иерархзии вызовов
VD>>Представил. Теперь потрудись объяснить причем тут C#?
VD>>В нем есть только два способа передать информацию — это глобальные переменные, и параметры методов. VD>>Ничего нового в C# 3.0 в этом плане не появится.
BZ>самое смешное, что в хаскеле — тоже для записи императивных алгоритмов в pure & lazy языке типа хаскела и clean используется фиктивное значение типа real world, передаваемое в и возвразаемое любой императивной процедурой.
real world — само по себе блулшит еще тот, но главное не это. C# не является pure & lazy. Он вообще не pure и не lazy.
Так что о каких монадах в нем можно вести речь я не пойму. Один прочел фразу, не въехал в нее и понеслась...
BZ>а do предоставляет для этого синтаксический сахар с закосом под императивные языки:
А зачем закос то? Пусть это будет нормальный императивный код. А вот это:
BZ>main = do ch1 <- getChar BZ> ch2 <- getChar BZ> putChar ch1
извни, я кроме как извратом назвать никак не могу.
BZ>а теперь представьте, что вы делаете другую монаду...
Да, мне по барабану монады. Пойми, не все тащатся от того что им имют мозг во все дыры. Кое-кто предпочитают простые и эффктивные решения.
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
EC>Монада — это контейнерный тип данных, который представляет собой экземпляр класса Monad,
EC>так же это экземпляр класса Functor, который декларирует наличие одной функции высшего порядка, преобразующей значения в контейнере в соответствии с некоторой функцией.
Это все попытки объяснить людям что такое монада в Хаскеле. Таких попыток море. И каждая из них исползует свои аналогии.
Фатк же остается фактом. Монада не имеет смысла в императивном языке. В C# есть объекты. У объектов есть состояние которое можно менять как угодно. Такое изменение будет всегда совершенно последовательным. Так что в ООП монада — это идиотизм.
EC>Как видишь довольно общая идея, применение которой можно найти не только в Haskell.
Эта "общая идея" была известна в математике. И Хаскель ее использует для прикрытия перехода в императивное вычисление. Да, в Хаскеле действительно нунжны некие объекты изменение которых происходит последовательно. Ведь обычне вычисления в Хаскеле не последовательныи не могут ничего изменить. Но в языке где и так можно мегять все что движется, а что не движется раскачивать и менять, это на фиг не упало.
EC>Механизмы LINQ (трансформация одной последовательности в другую) ложаться в её рамки, EC>а то, о чём ты говоришь не более чем частные случаи.
В концпцию монад ложится любое императивное вычисление. Это не странно. Или ты будешь утверждать, что чтение файла в C# — это тоже моды?
LINQ не более чем громкое название того что существовало сто лет до этого — вычислений с использованием фукнций высшего порядка и лямбд используемых для передачи конкретизирующих функций. Никакие монады для его обяснения не нужны.
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, lomeo, Вы писали:
VD>>Нет это их смысл. А вот области применения определяются этим смыслом.
L>Ну, кроме как посоветовать пойти почитать какие бывают монады мне больше нечего добавить.
Уже читал. И весе применения в конце концов приводят к последователному изменению состояния, т.е. императиву.
VD>>В общем, приведи плиз пример где в ООЯ были бы нужны монады.
L>Давай начну с отмазок — я не говорил, что они нужны в ООЯ, мало того, я даже не говорил, что они в ФЯ нужны
Но полез защищать ахинею о том, что в ООЯ что-то там реализовано на монадах?
L>Но раз ты сказал волшебное слово...
L>Монады можно использовать, например, в качестве альтернативы известным паттернам проектирования. Например, вместо NullObject можно взять мейби-монаду. У этого подхода есть определенные преимущества перед NullObject (впрочем, как и недостатки). В частности, нам не нужен отдельный нуль-объект.
Изивини, но почему в ОКамл и Немрле есть аналогичные решения без монад? Поместил объект в Some(...) и пользуйся, а если нужно извлечь, то паттерн матчинг к твоим услугам.
L>Да, и когда будешь отвечать на этот пример, вспомни, пожалуйста, что я не говорил, что монады "нужны", чтобы мы тут не нафлеймили (а то в последнее время у нас это здорово получается). Но вот обосновать их применение, наверное, можно. Раз уж люди их применяют.
Я вижу, что ты понимашь это. Но я так же вижу, что когда кто-то неразобравшись в вопросе понес ахинею, ты помог ему закрепить его заблуждения.
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
VD>Казалось бы. Пометь блок кода и скажи, что он должен выполняться последовательно и не лениво. И все! Пиши себе в других местах пользуясь всеми приемуществами линивости, а когда надо просто переключайся в императив. Но нет, мы создаем себе сказку и усилиенно пытаемся поверить в нее. В итоге получаем тоже самое, но сложно, запутано и медленно.
во-первых, там просто нет блоков кода. описания в чисто математическом ключе:
f x = let y = x*2
z = y+3
in y*z
т.е. вставить в такой набор мат. равенств вызовы процедур довольно проблематично. во-вторых, это потребовало бы больших переделок в языке и компиляторах, поскольку нарушило бы чистоту языка и независимость результата от порядка вычислений. т.е. пришлось бы вводить спец. правила взаимодействия императивного и чистого кода. фактически это и было сделано, но не на уровне изменения описания языка, а на уровне описания всех необходимых изменений средствами самого хаскела. и этот realWorld — это как раз и есть описание семантики императивных вычислений средствами семантики функциональной. и возмущаться этим — всё равно что предъявлять претензии к математикам вообще, которые придумывают столь сложные теории для описания того, как яблоки падают на землю (ты в курсе, что матанализ появился именно для описания движения планет по круговым орбитам?)
BulatZiganshin,
VD>>Казалось бы. Пометь блок кода и скажи, что он должен выполняться последовательно и не лениво. И все! Пиши себе в других местах пользуясь всеми приемуществами линивости, а когда надо просто переключайся в императив. Но нет, мы создаем себе сказку и усилиенно пытаемся поверить в нее. В итоге получаем тоже самое, но сложно, запутано и медленно.
BZ>во-первых, там просто нет блоков кода. описания в чисто математическом ключе:
BZ>
BZ>f x = let y = x*2
BZ> z = y+3
BZ> in y*z
BZ>
BZ>т.е. вставить в такой набор мат. равенств вызовы процедур довольно проблематично. во-вторых, это потребовало бы больших переделок в языке и компиляторах, поскольку нарушило бы чистоту языка и независимость результата от порядка вычислений. т.е. пришлось бы вводить спец. правила взаимодействия императивного и чистого кода.
Теперь и мне придётся выступить в роли оппонента
Я думаю, все отлично понимают отличия функций в языке программирования от функций в математике. В математике f: X->Y не предполагает, что f будет вычисляться, и вычисление займёт какое-то время. Вообще, понятия вычисления и времени нет, пока их явно не вводить.
Например, в математике (например, теория меры) запросто можно сказать: пусть U = f^{-1}(V) — прообраз множества V. Всё, дальше мы можем использовать множество U. И нас вообще не заботят следующие малозначительные детали как:
1. f (u) вычислить тяжело, практически невозможно, или даже теоретически невозможно;
2. f^{-1} (v) вычислить тяжело, практически невозможно, или даже теоретически невозможно;
3. Предикат u \in U вычислить тяжело, практически невозможно, или даже теоретически невозможно;
4. Предикат v \in V вычислить тяжело, практически невозможно, или даже теоретически невозможно;
5. Я уже не говорю, что сами множества U и V могут быть какими-нибудь неизмеримыми, или бесконечными, или бесконечномерными, или ещё какими-нибудь эдакими...
То есть я хочу сказать, что автоматически переносить понятие функции из математики в программирование может быть просто непрактично.
Вот теперь я вернусь обратно. Предположим, я ввожу соглашение (примерно то, что Влад предлагает): функции, которые в реализации используют нечистые конструкции, по соглашению будут начинаться с подчёркивания. Но чтобы избежать транзитивного замыкания грязи, мы вне таких функций будем пользоваться нашими старыми знакомыми — монадами:
_solve :: Matrix Int -> Vector Int -> Vector Int
_solve m b = -- далее идёт метод решения СЛАУ с матрицей m и правым столбцом b скажем методом LU
-- разложения, здесь можно использовать традиционный доступ к мутабельным массивам,
-- циклы и т.п.
-- компилятор рассматривает эту функцию как неделимую сущность
main :: IO () -> ()
main = do
(m, b) <- loadSLAE("slae.txt")
putStrLn (show $ _solve m b)
Таким образом мы можем устранить все критичные участки, что с _практической_ точки зрения благо. Разумеется, этот метод должен применяться только в качестве последнего средства (после ! и seq).
И что самое интересное, подобная возможность уже в Хаскеле есть — это FFI. Вот мне любопытно, что такого плохого станется, если поднять императив на уровень языка и использовать его в ограниченном контексте? Это несколько некомфортно с идеологической точки зрения, но практически полезно.
ps: (боже мой!!! неужели это сказал я!!! ААААА!!!)
Last Cjow Rhrr wrote: >
> И что самое интересное, подобная возможность уже в Хаскеле есть — это > FFI. Вот мне любопытно, что такого плохого станется, если поднять > императив на уровень языка и использовать его в ограниченном контексте? > Это несколько некомфортно с идеологической точки зрения, но практически > полезно. > > ps: (боже мой!!! неужели это сказал я!!! ААААА!!!)
Я не понял, чем твое предложение отличается от уже имеющейся монады ST.
Не развернешь мысль?
palm mute,
>> Вот мне любопытно, что такого плохого станется, если поднять >> императив на уровень языка и использовать его в ограниченном контексте?
PM>Я не понял, чем твое предложение отличается от уже имеющейся монады ST. PM>Не развернешь мысль?
Хочется внутри таких "преобразователей состояния" использовать обычное структурное программирование с обычными управляющими конструкциями.
Last Cjow Rhrr wrote: >> > Вот мне любопытно, что такого плохого станется, если поднять >> > императив на уровень языка и использовать его в ограниченном контексте? > > PM>Я не понял, чем твое предложение отличается от уже имеющейся монады ST. > PM>Не развернешь мысль? > > Хочется внутри таких "преобразователей состояния" использовать обычное > структурное программирование с обычными управляющими конструкциями.
, как легко
такие конструкции определить. Для применения в ограниченном контексте
вполне подходит. Гипотетические проблемы с лифтингом, которые мы
обсуждали ниже, возникнут, если этот ограниченный контекст раздуть, и в
этом случае действительно желательно рассмотреть вариант с FFI. Иметь 2
яызка в одном — не слишком хорошая идея, IMHO.
Здравствуйте, lomeo, Вы писали:
AVK>>И при чем тут LINQ? SelectMany это просто статический метод в классе Sequence. То что он extension method, это просто синтаксический сахар.
L>Не понял претензии... А bind это просто метод в классе Monad.
И что? Как это доказывает утверждение о сходстве LInQ и монад?
AVK>>Вобщем, идея о том, что LINQ это монады на мой вкус кажется крайне странной.
L>Я воспринял это как взгляд Erik Meijer. Почему бы и нет? Связка есть, монадические законы выполняются.
Здравствуйте, BulatZiganshin, Вы писали:
AVK>>На то есть паттерн ServiceLocator.
BZ>в хаскел-сообществе есть такая мысль, что монады — это наш способ делать паттерны
По поводу паттернов и их уместности здесь был топик, который начинался с сообщения о том, что паттерны суть недоделанность языка. Я там по этому поводу свое мнение уже высказывал. Повторяться не хочу.
BZ>а в чём конкретно состоит этот паттерн? если скажем в разветвлённой программе неожиланно высняется, что необходимо ещё и логгинг вызывать в некоторых местах, то что вы делаете?
Получаем в нужных местах сервис логгера и используем его. Логгер, кстати, чуть ли не хрестоматийный пример этого паттерна.
AVK>>>На то есть паттерн ServiceLocator.
BZ>>в хаскел-сообществе есть такая мысль, что монады — это наш способ делать паттерны
AVK>По поводу паттернов и их уместности здесь был топик, который начинался с сообщения о том, что паттерны суть недоделанность языка. Я там по этому поводу свое мнение уже высказывал. Повторяться не хочу.
да, так имхо и есть. есть методология программирования. есть язык. всё, что есть в этой методологии, но нет в конкретном языке, и становится паттернами программирования, рефакторинга, whatever
BZ>>а в чём конкретно состоит этот паттерн? если скажем в разветвлённой программе неожиланно высняется, что необходимо ещё и логгинг вызывать в некоторых местах, то что вы делаете?
AVK>Получаем в нужных местах сервис логгера и используем его. Логгер, кстати, чуть ли не хрестоматийный пример этого паттерна.
я и написал, что у нас есть два метода — либо всюду тащить это как параметр, либо сделать глобальным сервисом/переменной/whatever. фишка в том, что разные вычисления (например, разные треды) могут использовать два разных логгера. или парсить/генерить две разных программы. или вести сериализацию в два разных участка памяти. и тут ты натыкаешься на глобальные переменные в том или ином виде со всеми вытекающими последствиями. thread-local variables это обычно решают, но это всё обходной путь. монады как раз позволяют передавать параметры во все подчинённые процедуры, но делать это прозрачно, без изменения исходного кода. и позволяют при смене набора используемых "сервисов" просто обернуть код в ещё одну лишнюю монаду без разлборки с вновь появившимся параметром на всех уровнях
palm mute,
>> Хочется внутри таких "преобразователей состояния" использовать обычное >> структурное программирование с обычными управляющими конструкциями.
PM>Ты же сам видел
, как легко PM>такие конструкции определить. Для применения в ограниченном контексте PM>вполне подходит. Гипотетические проблемы с лифтингом, которые мы PM>обсуждали ниже, возникнут, если этот ограниченный контекст раздуть, и в PM>этом случае действительно желательно рассмотреть вариант с FFI.
+1, к сожалению. И поэтому я понимаю и разделяю Евгения и других активных пользователей С++ — у них такой проблемы нет. Пусть им тяжелее взбираться по "абстрактной лестнице" (перекладины шатаются, гвозди царапают мясо, сама лестница обладает нетривиальными топологическими свойствами и норовит завязаться в узел или образовать ленту Мёбиуса), но они могут в случае чего, контролировать поведение программы вооружившись микроскопом и делая отверстия для крепления подковы в перерывах между сердцебиениями. В принципе.
PM>Иметь 2 яызка в одном — не слишком хорошая идея, IMHO.
Хм. Отличие стиля от языка в одном и том же языке от меня ускользает...
Здравствуйте, BulatZiganshin, Вы писали:
BZ>я и написал, что у нас есть два метода — либо всюду тащить это как параметр, либо сделать глобальным сервисом/переменной/whatever. фишка в том, что разные вычисления (например, разные треды) могут использовать два разных логгера. или парсить/генерить две разных программы. или вести сериализацию в два разных участка памяти. и тут ты натыкаешься на глобальные переменные в том или ином виде со всеми вытекающими последствиями.
Здравствуйте, VladD2, Вы писали:
VD>Уже читал. И весе применения в конце концов приводят к последователному изменению состояния, т.е. императиву.
Некоторые монады вовсе не предназначены для изменения состояния.
Некоторые не предназначены для последовательного исполнения.
Простой пример — монада списка.
L>>Давай начну с отмазок — я не говорил, что они нужны в ООЯ, мало того, я даже не говорил, что они в ФЯ нужны :)
VD>Но полез защищать ахинею о том, что в ООЯ что-то там реализовано на монадах?
Ну, если на самом деле интересна моя мотивация, то она примерно такова.
Ты назвал человека некомпетентным, теперь называешь его мнение ахинеей. Я так не считаю, так почему бы не вложить свои 5 копеек?
L>>Монады можно использовать, например, в качестве альтернативы известным паттернам проектирования. Например, вместо NullObject можно взять мейби-монаду. У этого подхода есть определенные преимущества перед NullObject (впрочем, как и недостатки). В частности, нам не нужен отдельный нуль-объект.
VD>Изивини, но почему в ОКамл и Немрле есть аналогичные решения без монад? Поместил объект в Some(...) и пользуйся, а если нужно извлечь, то паттерн матчинг к твоим услугам.
Тут может быть много ответов. Но все они поведут к флейму. Я попытался обощить ниже.
L>>Да, и когда будешь отвечать на этот пример, вспомни, пожалуйста, что я не говорил, что монады "нужны", чтобы мы тут не нафлеймили (а то в последнее время у нас это здорово получается). Но вот обосновать их применение, наверное, можно. Раз уж люди их применяют.
VD>Я вижу, что ты понимашь это. Но я так же вижу, что когда кто-то неразобравшись в вопросе понес ахинею, ты помог ему закрепить его заблуждения.
Попробую донести свою мысль. Всё нижеследующее — это моё имхо. Монады — абстрактное понятие. В том смысле, что это гораздо абстрактнее понятия конкретного паттерна. Вот когда я описывал альтернативу для NullObject — я взял конкретную монаду. Т.е. говорить об использовании монады без указания конкретной монады, это почти как говорить об использовании паттерна без указания конкретного. "Почти" — потому что польза от этого всё таки есть. В силу того, что все они подчиняются определенным правилам, т.е. понятие достаточно формализовано.
Для того, чтобы на некоторое решение можно было бы взглянуть как на монадическое, достаточно, чтобы это решение соотвествовало нескольким правилам. Если эти правила соблюдаются, то, оперируя понятием монады, мы можем вывести некоторые свойства этого решения. А так — какая разница как называть это решение — монадой или нет — по барабану. Именно поэтому я считаю вопрос о нужности/ненужности монад бессмысленным.
Резюмируя (я тут в параллельных ветках твои ответы почитал). Разница в понимании монад у нас, видимо, следующая — для меня монада, это всего лишь интерфейс. Если его объявить явно, то мы поимеем унификацию всех монад, например, сможем иметь общие фунции над монадами, вместо того, чтобы писать для каждой свою. Если не объявлять — мы все равно можем заметить этот интерфейс, если он есть. И сделать для себя определенные выводы. Для тебя монада — это исключительно IO/State, которые, действительно, эмулируют императив. Эти монады в ИЯ не нужны.