Здравствуйте, BulatZiganshin, Вы писали:
BZ>честно говоря непонятно, что конкретно ты предлагаешь. в хаскеле и так уже есть чистые функции и, назовём их так, "процедуры". последние работают в IO monad. далее, функция не может вызывать процедуру, всё остальное возможно. таким образом, транзитивное замыкание приводит к тому, что внутри чистого кода может выззываться только чистый код (с поправкой на ST и тому подобные монады), а внутри императвиного — вс что угодно
Замечательно! Вот только это так можно сделать в любом языке! Надо только ввести пометку блоков кода как чисто функнциональные.
Проблема Хаскеля лишь в ленивости. Ведь если из ленивого кода вызвать императивный, то можно получить UB. Ведь код может не выполниться когда надо.
Меж тем линивость нужна далеко не всегда. Болшинство вычислений прекрасно пройдут в обычном ключе. Более того если программист сам будет определять, что лениво, а что нет, то и проблем у него не возникнет.
Почему в C# я пишу итератор в совершенно императивной манере:
IEnumerable<int> Seq(int start, int end)
{
for (int i = start; i <= end; i++)
yield return i;
}
и не получаю никаких пробелм (ни физический, ни идеологических)?
BZ>на уровне языка "процедуры" имеют тип (... -> IO a). в "IO Inside" описано как механизм типов делает невозможным выщов процедур из функций (окромя испольщования unsafePerformIO)
Это самовнушение. Физически проблем вызова "чистым кодом" "не чистого" нет. Ее придумали пуристы. И доказано это на практике.
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, lomeo, Вы писали:
L>Некоторые монады вовсе не предназначены для изменения состояния. L>Некоторые не предназначены для последовательного исполнения.
И что они тогда делают?
L>Простой пример — монада списка.
Тот же вопрос.
L>Ну, если на самом деле интересна моя мотивация, то она примерно такова. L>Ты назвал человека некомпетентным, теперь называешь его мнение ахинеей. Я так не считаю, так почему бы не вложить свои 5 копеек?
Потому что когда тебя начинпшь расспрашивать, то выясняется, что ты подтверждаешь мои слова.
VD>>Изивини, но почему в ОКамл и Немрле есть аналогичные решения без монад? Поместил объект в Some(...) и пользуйся, а если нужно извлечь, то паттерн матчинг к твоим услугам.
L>Тут может быть много ответов. Но все они поведут к флейму. Я попытался обощить ниже.
Ответы про монады, как сами монады — аморфны и выскальзают из рук. Никогда не получишь прямого и однозначного ответа.
L>Попробую донести свою мысль. Всё нижеследующее — это моё имхо. Монады — абстрактное понятие.
Классы/варинаты тоже. И что?
L> В том смысле, что это гораздо абстрактнее понятия конкретного паттерна.
Та же фингя.
L> Вот когда я описывал альтернативу для NullObject — я взял конкретную монаду.
А я конкретный вариант. И что?
L> Т.е. говорить об использовании монады без указания конкретной монады, это почти как говорить об использовании паттерна без указания конкретного.
Не совсем. Говорить о паттернах как раз можно. А вот монады снова слишком склизки для этого.
L> "Почти" — потому что польза от этого всё таки есть. В силу того, что все они подчиняются определенным правилам, т.е. понятие достаточно формализовано.
ОК. Я не хаскель-хакер и многое в нем не понимаю. Опиши эти правила как ты их ивдишь. За одно объясни какой в них смысл в языках где есть классы, объекты, варианты, их методы позволяющие менять их состояние...
L>Для того, чтобы на некоторое решение можно было бы взглянуть как на монадическое, достаточно, чтобы это решение соотвествовало нескольким правилам.
В эти правла не входит случаяно накомпление/изменение состояния?
L>Если не объявлять — мы все равно можем заметить этот интерфейс, если он есть. И сделать для себя определенные выводы.
У меня уже есть интерфейсы и типы. Зачем мне малопонятная сущьность с горой булшита вместо простых и понятных вещей?
L>Для тебя монада — это исключительно IO/State, которые, действительно, эмулируют императив. Эти монады в ИЯ не нужны.
Нет. Для меня монады — это совсем другое. Я вижу монады как подпорку под несостоятельной идеей чистоты. Они просто не нужны в "грязном", но реальном мире — мире где есть состояние и последовательность действий. В мире где прежде чем выпить чай нужно вскипятить воду и заварить заварку, а не где чай кипятится и заваривается когда я опракидываю пустую чашку себе в рот.
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, AndrewVK, Вы писали:
VD>>Не, скорее паттерн "передача объекта через параметр метода".
AVK>Нет. Передавать через параметры плохая идея. Сервисы появляются и умирают, зачастую динамически. Править каждый раз всю цепочку вызовов плохая идея.
Ты просто пришел с тем что тебя волнует. А то что обсуждается к этому отношения не имеет. Твои "сервисы" не более чем развитие идеи "супер навороченной глобальной переменной", а товарищи обсуждаеют передачу объекта через параметр. Понимаешь ли в чем дело, у этих товарищей бльшие проблемы как с наличием самого объекта, так и с возможностью изменения его состояния в нужной последовательнсоти. У них все вычисления чистые. Так что когда им надо тупо передать объект или изменить его состояние, то они вынуждные изобретать нечто под названием "хреновина", тфу ты, "монада", чтобы сделать вид, что они всего лишь трахнулись на стороне, а не изменили любимой.
Вот такие пироги с катятами. Но с глобальными переменными и уж тембоее с динамически подключаемыми сервисами у них проблем еще больще. Так что тут тоже будут хреновины... тфу ты монады.
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, Mirrorer, Вы писали:
M>Понятное дело что для языков, в которых массивы были отродясь Array реализованный через IList выглядит по меньшей мере извращением и мозго.. в общем brainfuck-ом. Но это не значит что сама идея интерфейса IList ущербна, и что классы List, Queue, Dictionary не нужны в языке поддерживающием массивы.
Дело в том, что в языках где "массивы были отродясь Array реализованный через IList" если бы их и не было, то их все равно реализовали бы через IList в виде класса. Так что вместо загадочных монад там есть объекты с состояниями которые решают все проблемы. А массивы захардкожены для эффективности.
M>Монада это интерфейс. КАК его использовать — личное дело каждого. И какие классы будут реализовывать этот интерфес в целом неважно. Важно то, что они будут иметь унифицированный интерфейс.
Я не спорю. Вот только в Хаскеле уже есть аналог интерфейса — это классы типов. Вот они спроектированы изумительно. И то что кроме них потребовались монады говорит о наличии проблем в идеологии.
Так вот в ИЯ таких проблем нет, а стало быть не нужны такие подпорки.
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, BulatZiganshin, Вы писали:
BZ>я и написал, что у нас есть два метода — либо всюду тащить это как параметр, либо сделать глобальным сервисом/переменной/whatever. фишка в том, что разные вычисления (например, разные треды) могут использовать два разных логгера. или парсить/генерить две разных программы. или вести сериализацию в два разных участка памяти. и тут ты натыкаешься на глобальные переменные в том или ином виде со всеми вытекающими последствиями. thread-local variables это обычно решают, но это всё обходной путь. монады как раз позволяют передавать параметры во все подчинённые процедуры, но делать это прозрачно, без изменения исходного кода. и позволяют при смене набора используемых "сервисов" просто обернуть код в ещё одну лишнюю монаду без разлборки с вновь появившимся параметром на всех уровнях
В ООП для этого есть классы. Создал класс и все его методы получают неявный контекст. Зачем изобретать велосипед?
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, BulatZiganshin, Вы писали:
BZ>и все объекты, вложенные в x, выполняя обычную операцию put, будут лить данные туда, куда им указали. с этим же паттерном, как я понимаю, надо настраивать этот сервис в *каждом* из сериализуемых объектов, входящих в x
Ну, и почему было не объявить интерфейс сериализации (IStream, например) вместо извращений и неявной магии? Далее реализуй его как FileStream или MemryStream и получай себе свой полиморфизм.
Хотя конечно вопрос риторический. Ведь в Хаскеле просто нельзя создать императивный объект. Вот и вынужден он паковать императивные объекты в монады. А в ООЯ то зачем это делать? Мы просто создаем нужный класс и все.
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, palm mute, Вы писали:
PM>Каким образом этот контекст попадает в точку вызова сервиса? Я PM>пробежался по описанию Фаулера, но сути не уловил. Ну, есть синглтон с PM>хэш-таблицей именованных объектов-сервисов (их еще и кастить надо, PM>тьфу), дальше что? Кто и как обеспечивает деление на уровни и доставку PM>контекста, в двух словах?
Блин, орлы, вы вообще там с со своим ФП офигели что ли? Человек думает в терминах ООЯ. И у него в подкорке лежит то что если он работает с потоками, то они поисываются классом. В этот класс можно запихнуть любое состояние и оно будет пренадлежать потоку. Оно будет всегда доступно. А считать это явной доступностью или нет уже дело вкуса.
Главное, что костыли вроде монад в ООЯ ненужны.
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, palm mute, Вы писали:
PM>Так об этом же и говорил Булат. С помощью монад можно избавиться от PM>передачи в аргументах метода или конструктора.
Спомощью полей объекто, тоже.
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, Mirrorer, Вы писали:
M>Монада это интерфейс. КАК его использовать — личное дело каждого. И какие классы будут реализовывать этот интерфес в целом неважно. Важно то, что они будут иметь унифицированный интерфейс.
Почему-то этому интерфейсу приписывают какую-то магическую сверхценность. Например, моноид тоже обладает офигительной общностью. Можно было бы сделать унифицированный интерфейс (plus,zero) для
чисел plus= +; zero = 0
списков plus= ++; zero = nil
функций plus= .; zero = id
преобразователей состояний итд.
но будет ли от того медведю польза и удовольствие?
VD>Блин, ну неужели трудно понять, что 90% здоровых на голву людей бросят сам язык когда дойдут до этого самого лифтинга. Это все равно что заливать в перегретый автомабиль воду из кипящего чайника!
а это не для начинающих для начала удобно как раз считать, что в хаскеле есть императивный и функциональный подязыки. я даже собирался написать "introduction to IO" в этом ключе, без всяких упоминаний о монадах. может, напишу его на русском
VD>И все это ради того чтобы не признать правды — мир императивен и "чистоту" надо просто отделять от "императивной грязи".
я не зря привёл пример с Ньютоном — он тоже отказался признавать, что планеты просто вращаются по эллиптическим орбитам и развёл целый мааппарат, позволяющий свести это к закону притяжения. всё это — лишь концепции. можно было сделать хаскелл двухконцептным языком, введя в него особые правила для записи императивных программ, их трансляции, оптимизации и т.д. а можно было — пользуясь средствами языка, выразить императивное программирование через функции, "преобразующие мир". при этом язык не раздулся как покойник и весь уже наработанный аппарат компиляторов спокойно работает с новой концепцией как частным случаем старой. ортогональность — это ключ к созданию хороших языков. "усложнять просто, упрощать — сложно"
VD>Эта концепция проста для понимания.
всякая концепция, которую ты уже понял, кажется простой для понимания скажем, я считаю функциональный подход более естественным и простым для понимания, нежели императивный и для меня такая программа:
fac 0 = 1
fac 1 = n * fac(n-1)
выглядит простой и ясной. а большинству людей, обученных императивному программированию, удобней и понятней запись в виде цикла. третьи же предпочитают higher-order funcs и запишут это в виде:
Здравствуйте, BulatZiganshin, Вы писали:
BZ>выглядит простой и ясной. а большинству людей, обученных императивному программированию, удобней и понятней запись в виде цикла. третьи же предпочитают higher-order funcs и запишут это в виде:
Угу, а тех кто запишет так (а главное будет думать в терминах подмен ):
VD>Я не спорю. Вот только в Хаскеле уже есть аналог интерфейса — это классы типов. Вот они спроектированы изумительно. И то что кроме них потребовались монады говорит о наличии проблем в идеологии.
Дык монады и реализованы на основе классов типов. Можешь считать монады патерном ФП.
Вот допустим при помощи монады List очень просто делаются list comprehensions. Разве это плохо?
Да, list comprehensions можно реализовать туевой хучей способов. Но решение на основе монад выглядит красиво имхо
Все три определения равносильны. Просто одни подсахарены больше другие меньше.
И второе имхо выглядит весьма достойно и понятно. А это ничто иное монада List.
Здравствуйте, Трурль, Вы писали:
Т>Почему-то этому интерфейсу приписывают какую-то магическую сверхценность.
Тут +10 я тоже не очень понимаю почему именно этот интерфейс раскручивается..
И почему его объяснение зачастую переусложенено.. Ведь большинство людей приходят в Хаскел из обычных императивных языков, и грузить им голову теорией категорий и говорить что "монады это уууу" не есть хорошо. Конечно на основе этого интерфейса можно навернуть очень сложные конструкции но для введения достаточно аналогии с IMonad<T>.
Но у меня мало опыта по работе с монадами, поэтому сложно что-то сказать, вот как (если) расковыряю Parsec, возможно увижу то, чего не видел раньше...
Т>Например, моноид тоже обладает офигительной общностью. Можно было бы сделать унифицированный интерфейс (plus,zero) Т>но будет ли от того медведю польза и удовольствие?
Наверное от задачи зависит ...
ИМХО. Монады — это паттерн ФП. Я думаю что не единственный. Просто так сложилось что вокруг него больше всего непоняток возникает.. Почему —
VD>И что они тогда делают?
L>>Простой пример — монада списка.
VD>Тот же вопрос.
VD>Ответы про монады, как сами монады — аморфны и выскальзают из рук. Никогда не получишь прямого и однозначного ответа.
По поводу монады списка.
Смотри.
Вот определение монады List
instance Monad [] where
m >>= f = concatMap f m
return x = [x]
fail s = []
ConcatMap делает следующее — делается map(f) для указанного списка, и результат конкатенируется.
public class List : IMonad
// грубо говоря делаем map функции f на списокpublic IMonad.Bind(m, f)
{
ConcateMap(f, m)
}
// оборачиваем X любого типа в список. Теперь Х просто так не достать.
//Он спрятан внутри монады (или класса List)
//и доступ к нему возможен только через интерфейсы,
//поддерживаемые классом List. В нашем случае это только IMonadpublic IMonad.Return(x);
{
return new List(x);
}
// А здесь просто возвращаем пустой список.public IMonad.Fail(s)
{
return new List();
}
Вот собственно и все.
There is no spoon.
Еще пришла в голову ассоциация.
В качестве примера рекурсии часто используется вычисление факториала. Но без tail оптимизации оно значительно менее эффективно чем императивное решение в лоб. Однако это не значит что сама идея рекурсии ущербна. Да, если реализовывать фаткориал в лоб, ничего хорошего не получится. Но ведь рекурсию еще можно применить в куче разных мест. Точно так же и с монадами. Ввод вывод в Хаскеле мне самому не шибко нравится. Он мне непривычен. Но та же монада List весьма интересная штука.
Здравствуйте, BulatZiganshin, Вы писали:
BZ>это вы рассужаете о своём паттерне. в моём примере использования монад речь шла о другом. есть универсальная процедура сериализации. куда сериализировать — она узнаёт из контекста вызова, т.е. из тех самых параметров. монады позволяют передавать эти паораметры неявно на всю глубину вызовов
То есть если я правильно понял, в данном случае аналогом в импетаривном языке будет дополнительный параметр у каждой процедуры по всей иерархии вызовов?
что-то типа
public void SomeAction(smth, context)
{
SomeAction1(smth, context);
}
public void SomeAction1(smth, context);
{
SomeAction2(smth, context);
}
public void SomeAction2(smth, context);
{
SomeAction3(smth, context);
}
public void SomeAction3(smth, context);
{
smth.Serialize(context);
}
Причем Context это что-то типа хеш таблицы куда можно запихивать сколько угодно барахла, и нужный кусок барахла будет использован на уровне для него предназначенном?
ну вот и на C монады реализовали
M>В качестве примера рекурсии часто используется вычисление факториала. Но без tail оптимизации оно значительно менее эффективно чем императивное решение в лоб. Однако это не значит что сама идея рекурсии ущербна. Да, если реализовывать фаткориал в лоб, ничего хорошего не получится. Но ведь рекурсию еще можно применить в куче разных мест. Точно так же и с монадами. Ввод вывод в Хаскеле мне самому не шибко нравится. Он мне непривычен. Но та же монада List весьма интересная штука.
самое красивое применение монад — parsec и люббые другие parsing combinators. там они используются для бэктрекинга при неудачном разборе, т.е. запросто можно запистать что-то вроде:
что касается в/в — то читай http://haskell.org/haskellwiki/IO_inside . то, что императивные операторы в хаскеле — частный случай обычных функций, даёт возможность применять к ним весь арсенал higher-order funcs. там как раз приводятся примеры того, как императивные процедуры хранятся в списках, передаются туда-сюда, частично применяются и тому подобное. только что я приводил пример их композиции в виде отдельно запускаемых тредов. у меня в программе вообще куча универсальных управляющих конструкций и просто по месту определяются процедуры, которые скажем файлы после обработки подчищают. т.е. использование процедур как first-class objects позволяет лучше структурировать программу
Здравствуйте, Mirrorer, Вы писали:
M>Здравствуйте, BulatZiganshin, Вы писали:
BZ>>это вы рассужаете о своём паттерне. в моём примере использования монад речь шла о другом. есть универсальная процедура сериализации. куда сериализировать — она узнаёт из контекста вызова, т.е. из тех самых параметров. монады позволяют передавать эти паораметры неявно на всю глубину вызовов
M>То есть если я правильно понял, в данном случае аналогом в импетаривном языке будет дополнительный параметр у каждой процедуры по всей иерархии вызовов?
да. написанное вами — это монада Environment, позволяющая только читать обобществлённые данные. насчёт хеша же вы не совсем поняли. вы просто моджете передавать любую структуру данных, полиморфизм и вывод типов делает это так же возможным, как если бы вы написали специальный код
пример:
calculate = withState $ do
calc1
x <- calc2
y <- calc3
return (x++y)
calc1 = put (1, 2.3, "рояль")
calc2 = do (a,b,c) <- get
put (4, 5.6, " в кустах")
return c
calc3 = do (a,b,c) <- get
return c
вот это вычисление возвратит рояль в кустах при этом, хотя явно это не записано, фактически каждая операция получает эту триаду в качестве входного параметра и возвращзает её в качестве выходного. операция put заменяет старый state своим аргументом и возвращет () в качестве реузультат своего выполнения:
put param state = (param,())
операция get возвращает state в качестве результата своего выполнения и при этом, естественно, сохраняет сам state:
get state = (state,state)
остальные операции просто передают state дальше и дальше без изменений. все монадические операции, определяемые здесь — calc1/2/3 получают state в качестве дополнительного параметра и возвразают, помимо результата своей работы, новое состояние state, которое будет передано следующей операции в цепочке. скажем, расшугарив calculate, мы получим:
вот это уже монада State, котопая по принципам своего устройстьва напоминает IO. приведённая вами монада Environ,ent, которая подращзумевает только чтение входных данных, может обойтись без вохзвращения новго state из выполлняемых функций:
calculate = withEnvironment $ env ->
let _ = calc1 env
x = calc2 env
y = calc3 env
in (x++y)
как видите, в ней в монадические операции получают env в качестве входного параметра, но не возвразают его новое значение. дальше они также невидлимо передают его в качестве дополнительного параметра во все выхзываемые ими монадические операции