Здравствуйте, Maxim S. Shatskih, Вы писали:
DC>>Таким образом, получается operator << и operator >> создают монадный интерфейс ввода/вывода в императивном языке.
MSS>Если я правильно понял хаскеллевские монады, то это как раз _привнесение императивности_ в неимперативный язык.
Имхо ты понял лишь частный случай применения монад.
Здравствуйте, FR, Вы писали:
MSS>>Таким образом, параллелей в Си++ монада не имеет из-за отсутствия в языке клозур (лямбду можно сделать указателем на функцию, но без клозуры она не интересна).
FR>Замыкание без проблем эмулируется классом — функтором.
А класс-функтор без проблем эмулируется структурами и указателями на фунции, т.е. структрным программированием.
А структрное программирование без проблем эмулируется с помощью ассемблера.
Да здравствует ассемблер!
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, dr.Chaos, Вы писали:
DC>Я понял что ты про определение в ТК, но я туда даже не заглядывал. И подозреваю, что очень отдаленно представляю, что такое функтор и морфизм. Посему увидеть их мне достаточно трудно.
Если есть несколько минут — можно начать отсюда: http://en.wikibooks.org/wiki/Haskell/Category_theory
Там очень просто и как раз про функторы и монады.
DC>Если я правильно понял то для класса типов Monad морфизм unit — это return, а join — это >>=.
return — да, это unit. >>= — это bind (:: m a -> (a -> m b) -> m b)
а join — это join (:: m (m a) -> m a). Он определён в Control.Monad.
Связь между ними такая:
join m = m >>= id
m >>= f = join (fmap f m)
L>>Покажи. Я пока не догнал, о чём ты. У манипуляторов тип не похож на bind, IMHO. Ты не об этом?
DC>В той теме Булат рассахаривал IO монаду, вот если представить так выполнение цепочки операторов <<, то выглядит очень похоже.
DC>
DC>Просто при использовании ФВП монаду совсем не видно.
Понял, меня что смущает — в bind мы связываем монаду с обработкой значения того типа, который эта монада оборачивает.
Т.е. если монада (m a), то обработчик у нас (a -> m b).
У тебя же это значение (:: a) содержит изначально сам обработчик (Out(5)).
Если говорить о схожести, это по моему единственное отличие. Так что больше не буду цепляться
Вобщем смотрю я на монады и думаю, что все это, что-то мне напоминает.
Возьмем в С++ operator <<(). Он принимает basic_ostream и возвращает его только в обновленном состоянии, т.е. по сути это операция связывания, а вот действие которое связано спрятано в перегрузках оператора, т.е. по умолчанию это функция преобразования некоторого типа в поток байт. Есть там и действия которые передаются явно (манипуляторы потока). Можно кстати и вывод примерно так представить, но это будет выглядеть некрасиво
Таким образом, получается operator << и operator >> создают монадный интерфейс ввода/вывода в императивном языке.
Правда тип bind m a->(a->m b)->m b несколько не стыковывается, т.к а и б тут одинаковые, а operator<< принимает не поток, а другой тип. Хотя поток байт можно считать базовым для всех типов или точнее у каждого типа передаваемого в operator << есть преобразование в поток.
Если я ничего не напутал, получается что в имеперативных языках монады таки используются. Причем потоковый ввод/вывод одно из самых красивых, гибких и практичных решений в STL. Правда я сомневаюсь, что создатель знал про монады.
Побеждающий других — силен,
Побеждающий себя — Могущественен.
Лао Цзы
Здравствуйте, dr.Chaos, Вы писали:
DC>Вобщем смотрю я на монады и думаю, что все это, что-то мне напоминает.
DC>Возьмем в С++ operator <<(). Он принимает basic_ostream и возвращает его только в обновленном состоянии, т.е. по сути это операция связывания, а вот действие которое связано спрятано в перегрузках оператора, т.е. по умолчанию это функция преобразования некоторого типа в поток байт. Есть там и действия которые передаются явно (манипуляторы потока). Можно кстати и вывод примерно так представить, но это будет выглядеть некрасиво
Прикольно. Но, мне кажется, это не так. Не буду сейчас говорить о том, что тип не совпадает для bind, но есть одно из отличительных свойств у монад. Это свойство вытекает из ТК-определения монады. Оно говорит о том, что у монады должен быть join.
(Вообще монада -- это функтор с двумя морфизмами — unit :: X -> M X, join :: M (M X) -> M X, где X — это любой объект категории С, над которой определён функтор M).
Так вот, если мы видим, что наш тип, являясь функтором, имеет join, значит, скорее всего это монада. Почему join, а не bind? Мне кажется, потому что его проще увидеть.
Так вот, в случае LINQ во первых — сразу видно, что это функтор (select prop from obj_list -- явный map). Во вторых, связка списков (таблиц, множеств) есть join, он даже в SQL так называется Из объектов одного перечисления мы связкой получаем другие перечисления, а потом объединяем в одно: т.е. IEnumerable<IEnumerable<T>> -> IEnumerable<T>.
Здравствуйте, lomeo, Вы писали:
L>Здравствуйте, dr.Chaos, Вы писали:
DC>>Вобщем смотрю я на монады и думаю, что все это, что-то мне напоминает.
DC>>Возьмем в С++ operator <<(). Он принимает basic_ostream и возвращает его только в обновленном состоянии, т.е. по сути это операция связывания, а вот действие которое связано спрятано в перегрузках оператора, т.е. по умолчанию это функция преобразования некоторого типа в поток байт. Есть там и действия которые передаются явно (манипуляторы потока). Можно кстати и вывод примерно так представить, но это будет выглядеть некрасиво
L>Прикольно. Но, мне кажется, это не так. Не буду сейчас говорить о том, что тип не совпадает для bind, но есть одно из отличительных свойств у монад. Это свойство вытекает из ТК-определения монады. Оно говорит о том, что у монады должен быть join.
L>(Вообще монада -- это функтор с двумя морфизмами — unit :: X -> M X, join :: M (M X) -> M X, где X — это любой объект категории С, над которой определён функтор M).
Погоди, что есть морфизм, а то я не пойму о чем ты говоришь.
L>Так вот, если мы видим, что наш тип, являясь функтором, имеет join, значит, скорее всего это монада. Почему join, а не bind? Мне кажется, потому что его проще увидеть.
L>Так вот, в случае LINQ во первых — сразу видно, что это функтор (select prop from obj_list -- явный map). Во вторых, связка списков (таблиц, множеств) есть join, он даже в SQL так называется Из объектов одного перечисления мы связкой получаем другие перечисления, а потом объединяем в одно: т.е. IEnumerable<IEnumerable<T>> -> IEnumerable<T>.
L>В случае ostream что есть join?
Что я увидел, так это "неявное" протаскивание параметра по цепочке.
Тут еще такое дело получается что если operator << () параметризовать функтором char->stream<char>, то его реализацию можно будет менять для всех типов. Т.е. это что-то типа перегрузки оператора последовательности.
Просто я в монады только въезжаю, но тут я вижу схожесть того что получаем с помощью монад с тем что сделано в STL. А вот является ли это монадой — .
Побеждающий других — силен,
Побеждающий себя — Могущественен.
Лао Цзы
L>>(Вообще монада -- это функтор с двумя морфизмами — unit :: X -> M X, join :: M (M X) -> M X, где X — это любой объект категории С, над которой определён функтор M).
DC>Погоди, что есть морфизм, а то я не пойму о чем ты говоришь.
Я говорю о том, что понятие "монада" имеет чёткое математическое определение в теории категорий. Следовательно, если случай подпадает под это определение, значит это монада, иначе нет. Грубо говоря, если любой тип можно завернуть в наш, и если из дважды завернутого в наш тип можно сделать единожды, то это монада. Это очень примитивно, на самом деле там ещё несколько условий, но это те, по которым обычно можно сориентироваться.
L>>В случае ostream что есть join?
DC>Что я увидел, так это "неявное" протаскивание параметра по цепочке.
Покажи. Я пока не догнал, о чём ты. У манипуляторов тип не похож на bind, IMHO. Ты не об этом?
DC>Тут еще такое дело получается что если operator << () параметризовать функтором char->stream<char>, то его реализацию можно будет менять для всех типов. Т.е. это что-то типа перегрузки оператора последовательности.
DC>Просто я в монады только въезжаю, но тут я вижу схожесть того что получаем с помощью монад с тем что сделано в STL. А вот является ли это монадой — .
Схожесть — да, наверное, есть. Но, мне кажется, это не монада.
Здравствуйте, lomeo, Вы писали:
DC>>Тут еще такое дело получается что если operator << () параметризовать функтором char->stream<char>, то его реализацию можно будет менять для всех типов. Т.е. это что-то типа перегрузки оператора последовательности.
DC>>Просто я в монады только въезжаю, но тут я вижу схожесть того что получаем с помощью монад с тем что сделано в STL. А вот является ли это монадой — .
L>Схожесть — да, наверное, есть. Но, мне кажется, это не монада.
Если бы ostream после вывода туда char становился бы ostream<char>, после вывода int — ostream<int>, а после вывода CSomeClass — ostream<CSomeClass> (т.е. если бы тип фозвращаемого результата определялся бы типом параметра последнего вызова) — то это была бы монада. А так мы имеем результат применения монады к char (не забудем, что монада — это конструктор типов). Примерный эквивалент ostream на Хаскеле будет такой:
type Ostream a = WriterT [a] IO ()
а отнюдь не
type Ostream a = IO а
Приведенная первой конструкция вполне себе может использваться в Хаскелевских EDSL, и она сделана на монадах — но она не монада.
Здравствуйте, Dusty, Вы писали:
D>Если бы ostream после вывода туда char становился бы ostream<char>, после вывода int — ostream<int>, а после вывода CSomeClass — ostream<CSomeClass> (т.е. если бы тип фозвращаемого результата определялся бы типом параметра последнего вызова) — то это была бы монада. А так мы имеем результат применения монады к char (не забудем, что монада — это конструктор типов).
Что такое "применение монады к char"?
D>Примерный эквивалент ostream на Хаскеле будет такой: D>
D> type Ostream a = WriterT [a] IO ()
D>
Почему?
D>Приведенная первой конструкция вполне себе может использваться в Хаскелевских EDSL, и она сделана на монадах — но она не монада.
Почему не монада? Это же полный аналог IO [a] и значит можно
newtype Ostream a = Ostream { runOstream :: IO [a] }
instance Functor Ostream where
fmap f (Ostream io) = Ostream $
do xs <- io
return (fmap f xs)
instance Monad Ostream where
return x = Ostream (return [x])
(Ostream io) >>= f = Ostream $
do xs <- io
liftM concat $ runOstream $ mapM f xs
Здравствуйте, lomeo, Вы писали:
L>>>(Вообще монада -- это функтор с двумя морфизмами — unit :: X -> M X, join :: M (M X) -> M X, где X — это любой объект категории С, над которой определён функтор M).
DC>>Погоди, что есть морфизм, а то я не пойму о чем ты говоришь.
L>Я говорю о том, что понятие "монада" имеет чёткое математическое определение в теории категорий. Следовательно, если случай подпадает под это определение, значит это монада, иначе нет. Грубо говоря, если любой тип можно завернуть в наш, и если из дважды завернутого в наш тип можно сделать единожды, то это монада. Это очень примитивно, на самом деле там ещё несколько условий, но это те, по которым обычно можно сориентироваться.
Я понял что ты про определение в ТК, но я туда даже не заглядывал. И подозреваю, что очень отдаленно представляю, что такое функтор и морфизм. Посему увидеть их мне достаточно трудно.
Если я правильно понял то для класса типов Monad морфизм unit — это return, а join — это >>=.
L>>>В случае ostream что есть join?
DC>>Что я увидел, так это "неявное" протаскивание параметра по цепочке.
L>Покажи. Я пока не догнал, о чём ты. У манипуляторов тип не похож на bind, IMHO. Ты не об этом?
В той теме Булат рассахаривал IO монаду, вот если представить так выполнение цепочки операторов <<, то выглядит очень похоже.
Передать туда мы можем любой функтор, а саму bind переопределить как нам надо.
template<typename T>
struct Out
{
Out(T p)
: m_param(toStr(p)),
: m_size(strlen(m_param))
{}
stream& operator() (stream& s)
{
s.write(m_param,m_size);
return s;
}
private:
char* m_param;
long m_size;
};
template<typename T>
struct In
{
In(T &p)
: m_param(p),
{}
stream& operator() (stream& s)
{
int N = sizeof(T);
char * raw = new char[N];
s.read(raw,N);
m_param = fromStr(raw);
return s;
}
private:
T& m_param;
};
//В таком случае
//bind(Out(5),cout) <=> cout << 5;
// а
//int n;
//bind(In(n),cin) <=> cin >> n;
Те конкретная операция намертво зашита в соответствующих операторах.
DC>>Тут еще такое дело получается что если operator << () параметризовать функтором char->stream<char>, то его реализацию можно будет менять для всех типов. Т.е. это что-то типа перегрузки оператора последовательности.
DC>>Просто я в монады только въезжаю, но тут я вижу схожесть того что получаем с помощью монад с тем что сделано в STL. А вот является ли это монадой — .
L>Схожесть — да, наверное, есть. Но, мне кажется, это не монада.
До монады не доделали . Но вот направление явно в сторону монад.
Побеждающий других — силен,
Побеждающий себя — Могущественен.
Лао Цзы
Здравствуйте, lomeo, Вы писали:
L>Здравствуйте, Dusty, Вы писали:
D>>А так мы имеем результат применения монады к char (не забудем, что монада — это конструктор типов).
L>Что такое "применение монады к char"?
В терминах С++ — результат инстанцирования шаблона с соответствующим аргументом. В терминах Хаскеля — применение конструктора типов (который является экземпляром класса Monad) к типу. Т.е. IO a — монада, IO Char — результат применения монады IO к Char...
Может, выразился неудачно — есть для этого стандартная терминология?
D>>Примерный эквивалент ostream на Хаскеле будет такой: D>>
D>> type Ostream a = WriterT [a] IO ()
D>>
L>Почему?
Я вижу два следующих сближающих признака:
1. Протягиваем между вызовами объект потока (явно в С++, неявно в Хаскеле) — и только его.
2. Независимо от типа переданных параметров, функция, возвращающая Ostream a в конечном итоге сводит все к (cenzored) а.
Для настоящей монады оба эти ограничения не работают.
D>>Приведенная первой конструкция вполне себе может использваться в Хаскелевских EDSL, и она сделана на монадах — но она не монада.
L>Почему не монада? Это же полный аналог IO [a] и значит можно
(skip) L>Законы соблюдаются.
Для IO [a] законы-то соблюдаются... А вот попробуй написать соответствующие инстансы для моего определения Ostream.
Здравствуйте, Dusty, Вы писали:
L>>Что такое "применение монады к char"?
D>В терминах С++ — результат инстанцирования шаблона с соответствующим аргументом. В терминах Хаскеля — применение конструктора типов (который является экземпляром класса Monad) к типу. Т.е. IO a — монада, IO Char — результат применения монады IO к Char... D>Может, выразился неудачно — есть для этого стандартная терминология?
Угу, правильно. Это кажется type application и называется, я просто не понял, о чём ты.
D>>>Примерный эквивалент ostream на Хаскеле будет такой: D>>>
D>>> type Ostream a = WriterT [a] IO ()
D>>>
L>>Почему? D>Я вижу два следующих сближающих признака: D>1. Протягиваем между вызовами объект потока (явно в С++, неявно в Хаскеле) — и только его. D>2. Независимо от типа переданных параметров, функция, возвращающая Ostream a в конечном итоге сводит все к (cenzored) а. D>Для настоящей монады оба эти ограничения не работают.
Всё равно не понял. Как из этого вытекает тип \a -> WriterT [a] IO ()?
L>>Законы соблюдаются. D>Для IO [a] законы-то соблюдаются... А вот попробуй написать соответствующие инстансы для моего определения Ostream.
OK, только я сделаю его новым типом, а не синонимом.
У синонимов проблемы с type application.
newtype Ostream a = Ostream { runOstream :: WriterT [a] IO () }
ioToOstream = Ostream . WriterT
ostreamToIo = runWriterT . runOstream
instance Functor Ostream where
fmap f os = ioToOstream $
do ((), xs) <- ostreamToIo os
return ((), fmap f xs)
instance Monad Ostream where
return x = ioToOstream (return ((), [x]))
os >>= f = ioToOstream $
do ((), xs) <- ostreamToIo os
liftM (\((), xs) -> ((), concat xs)) $ ostreamToIo $ mapM f xs
Это практически тот же код, и он также соблюдает законы, т.к. IO [a] изоморфно WriterT [a] IO () по "a".
Здравствуйте, lomeo, Вы писали:
L>Здравствуйте, Dusty, Вы писали:
D>>Может, выразился неудачно — есть для этого стандартная терминология? L>Угу, правильно. Это кажется type application и называется, я просто не понял, о чём ты.
Да по английски-то понятно... А по русски — устоявшегося термина нет.
D>>Я вижу два следующих сближающих признака: D>>1. Протягиваем между вызовами объект потока (явно в С++, неявно в Хаскеле) — и только его. D>>2. Независимо от типа переданных параметров, функция, возвращающая Ostream a в конечном итоге сводит все к (cenzored) а. D>>Для настоящей монады оба эти ограничения не работают. L>Всё равно не понял. Как из этого вытекает тип \a -> WriterT [a] IO ()?
Понятно, что не вытекает автоматом. Просто я а) сформулировал, что мне не нравится в ostream как монаде, б) наткнулся — случайно — на эту аналогию, в) привел его. Может, она и не правильная, но мне помогает...
Кстати: речь у меня идет не о a -> WriterT [a] IO (), а о b -> WriterT [a] IO () (иначе непонятно, с чего бы пункт 2)
L>>>Законы соблюдаются. D>>Для IO [a] законы-то соблюдаются... А вот попробуй написать соответствующие инстансы для моего определения Ostream.
L>OK, только я сделаю его новым типом, а не синонимом. L>У синонимов проблемы с type application.
[skip]
Понял. Красивый код...
Но — мне кажется — что перейдя к новому типу, ты разрушил суть моей аналогии. А поскольку это именно аналогия, а не точная эквивалентность — то и смысл потерялся.
Что будет делать твой код, если его перевести на С++? Фаткически — перекодировать файлы (точнее — содержимое потоков) : из ostream<wchar> в ostream<char>, из ostream<char> в ostream<edbic_char> и т.д.
В то же время operator<< перекодирует все, что угодно в char (или wchar — в зависимости от типа потока).
Хотя... Я тут подумал: вывод значений в поток действительно не меняет его типа. Но вот использование манипуляторов — в некотором смысле меняет; только это не отражается в системе типов С++.
В этом смысле можно смотреть на ostream и как на монаду... Но лично мне кажется все-таки, что такой взгляд — контрпродуктивен.
Здравствуйте, Dusty, Вы писали:
D>Кстати: речь у меня идет не о a -> WriterT [a] IO (), а о b -> WriterT [a] IO () (иначе непонятно, с чего бы пункт 2)
А что a не может таким, же как и b?
L>>>>Законы соблюдаются. D>>>Для IO [a] законы-то соблюдаются... А вот попробуй написать соответствующие инстансы для моего определения Ostream.
L>>OK, только я сделаю его новым типом, а не синонимом. L>>У синонимов проблемы с type application. D>[skip] D>Понял. Красивый код... D>Но — мне кажется — что перейдя к новому типу, ты разрушил суть моей аналогии. А поскольку это именно аналогия, а не точная эквивалентность — то и смысл потерялся.
D>Что будет делать твой код, если его перевести на С++? Фаткически — перекодировать файлы (точнее — содержимое потоков) : из ostream<wchar> в ostream<char>, из ostream<char> в ostream<edbic_char> и т.д. D>В то же время operator<< перекодирует все, что угодно в char (или wchar — в зависимости от типа потока).
Ну если взять iostream, то есть и обратное преобразование.
D>Хотя... Я тут подумал: вывод значений в поток действительно не меняет его типа. Но вот использование манипуляторов — в некотором смысле меняет; только это не отражается в системе типов С++. D>В этом смысле можно смотреть на ostream и как на монаду... Но лично мне кажется все-таки, что такой взгляд — контрпродуктивен.
Ну тут сама суть ИМХО важна, народ попытался получить гибкое решение, и получил его. Вот только решение очень на монаду смахивает.
Побеждающий других — силен,
Побеждающий себя — Могущественен.
Лао Цзы
Здравствуйте, Dusty, Вы писали:
D>Понятно, что не вытекает автоматом. Просто я а) сформулировал, что мне не нравится в ostream как монаде, б) наткнулся — случайно — на эту аналогию, в) привел его. Может, она и не правильная, но мне помогает...
D>Кстати: речь у меня идет не о a -> WriterT [a] IO (), а о b -> WriterT [a] IO () (иначе непонятно, с чего бы пункт 2)
Ты написал:
type Ostream a = WriterT [a] IO ()
а не
type Ostream b = forall a. WriterT [a] IO ()
Поэтому я и подумал. А так — да.
D>Но — мне кажется — что перейдя к новому типу, ты разрушил суть моей аналогии. А поскольку это именно аналогия, а не точная эквивалентность — то и смысл потерялся.
Да, да, если "b", а не "a", то это совсем не то
D>Что будет делать твой код, если его перевести на С++? Фаткически — перекодировать файлы (точнее — содержимое потоков) : из ostream<wchar> в ostream<char>, из ostream<char> в ostream<edbic_char> и т.д. D>В то же время operator<< перекодирует все, что угодно в char (или wchar — в зависимости от типа потока).
т.е. это класс Show
Если серьёзно, то dr.Chaos увидел аналогию с монадой, а не саму монаду, а я это неверно понял и стал доказывать, что он ошибается.
D>Хотя... Я тут подумал: вывод значений в поток действительно не меняет его типа. Но вот использование манипуляторов — в некотором смысле меняет; только это не отражается в системе типов С++. D>В этом смысле можно смотреть на ostream и как на монаду... Но лично мне кажется все-таки, что такой взгляд — контрпродуктивен.
Здравствуйте, lomeo, Вы писали:
L>Здравствуйте, Dusty, Вы писали:
D>>Кстати: речь у меня идет не о a -> WriterT [a] IO (), а о b -> WriterT [a] IO () (иначе непонятно, с чего бы пункт 2)
L>Ты написал:
type Ostream a = WriterT [a] IO ()
L>а не
type Ostream b = forall a. WriterT [a] IO ()
L> Поэтому я и подумал. А так — да.
Не-не-не! Я говорил о том, что С++сному
ostream<char>& operator<<(ostream<char>&, int);
должно соответствовать Хаскелевское
haskell]
(<<) :: Int -> WriterT [Char] IO ()
[/haskell]
Мы говорим о самом классе/типе потока, и забываем, что операции вывода в него — они с параметрами...
А экзистенциальные типы я отнюдь не имел в виду...
D>>Что будет делать твой код, если его перевести на С++? Фаткически — перекодировать файлы (точнее — содержимое потоков) : из ostream<wchar> в ostream<char>, из ostream<char> в ostream<edbic_char> и т.д. D>>В то же время operator<< перекодирует все, что угодно в char (или wchar — в зависимости от типа потока).
L>т.е. это класс Show
Угу, в некотором роде
L>Если серьёзно, то dr.Chaos увидел аналогию с монадой, а не саму монаду, а я это неверно понял и стал доказывать, что он ошибается.
D>>Хотя... Я тут подумал: вывод значений в поток действительно не меняет его типа. Но вот использование манипуляторов — в некотором смысле меняет; только это не отражается в системе типов С++. D>>В этом смысле можно смотреть на ostream и как на монаду... Но лично мне кажется все-таки, что такой взгляд — контрпродуктивен.
L>+1
Здравствуйте, dr.Chaos, Вы писали:
DC>Здравствуйте, Dusty, Вы писали:
D>>Кстати: речь у меня идет не о a -> WriterT [a] IO (), а о b -> WriterT [a] IO () (иначе непонятно, с чего бы пункт 2)
DC>А что a не может таким, же как и b?
а может быть таким же, как и b. Но а не может быть не таким, как а.
DC>Ну тут сама суть ИМХО важна, народ попытался получить гибкое решение, и получил его. Вот только решение очень на монаду смахивает.
Только что на IO/ST. На Maybe или List — уже нет, а ведь это тоже примеры монад (если так можно выразиться — "неимперативных").
К>Имхо ты понял лишь частный случай применения монад.
Возможно. Но монадный bind, как я помню, возвращает _лямбду_, при вызове которой возвращается уже объект с измененным состоянием. Си++ ostream::operator<<() возвращает сразу объект.
Таким образом, параллелей в Си++ монада не имеет из-за отсутствия в языке клозур (лямбду можно сделать указателем на функцию, но без клозуры она не интересна).
Здравствуйте, Maxim S. Shatskih, Вы писали:
MSS>Таким образом, параллелей в Си++ монада не имеет из-за отсутствия в языке клозур (лямбду можно сделать указателем на функцию, но без клозуры она не интересна).
Замыкание без проблем эмулируется классом — функтором.
FR>>Замыкание без проблем эмулируется классом — функтором.
VD>А класс-функтор без проблем эмулируется структурами и указателями на фунции, т.е. структрным программированием.
VD>А структрное программирование без проблем эмулируется с помощью ассемблера.
VD>Да здравствует ассемблер!
Я думаю, что интерпретатор Хаскеля именно так и реализован.