Здравствуйте, alex_public, Вы писали:
_>Да, и пока это у нас ещё не монады, а просто функторы. Чтобы потребовались монады, у нас например функция parse должна оказаться изначально написанной под boost.optional. Т.е. иметь прототип вида: _>
_>optional<int> parse(const string& s)
_>
_>Чтобы наш код продолжил работать и с такой функцией, нам надо будет добавить (хотя в данном случае можно и заменить им старый вариант) ещё один вариант оператора>>: _>
_>Это и будет та самая монада... Причём как видно, она даже проще чем обычные функторы.
Это будет монада, но это будет отстой, потому что в более менее серьезном приложении придется понавыписывать столько операторов, что вспотеешь только перечислять их.
Монады же нужны для того, что бы не надо было этим заниматься, то есть, вообще.
Собственно, в гибридах это достижимо, правда, не любую задачу можно толково завернуть в монады.
Re[13]: Есть ли вещи, которые вы прницпиально не понимаете...
Здравствуйте, Ikemefula, Вы писали:
I>Это будет монада, но это будет отстой, потому что в более менее серьезном приложении придется понавыписывать столько операторов, что вспотеешь только перечислять их.
С чего бы это? Все типы не фиксированы, так что достаточно одной версии.
I>Монады же нужны для того, что бы не надо было этим заниматься, то есть, вообще.
Это понятно. Не понятно другое — нафига нам вообще задаваться целью строить подобные конвейеры, если и без них всё отлично пишется. Причём код получается даже более простой и очевидный.
Re[11]: Есть ли вещи, которые вы прницпиально не понимаете...
НеВсеТак<Легко> СейчасВсеБудет<Легко, Просто, НеВсеТак>(this Легко x, Func<Легко, НеВсеТак<Просто>> f)
K>Камень преткновения здесь в выделенном. Такого система типов C# не позволяет.
Не, при известном желании всё можно:
public interface IMonad<T, TMonad> where TMonad: IMonad<T, TMonad>
{
TMonad Make(T value);
TMonad Bind(TMonad monad, Func<T, T> apllyFunc);
T Get(TMonad monad);
}
public class Easy { }
public class Simple { }
public class NotSo<T>: IMonad<T, NotSo<T>>
{
public NotSo<T> Make(T value)
{
...
}
public NotSo<T> Bind(NotSo<T> monad, Func<T, T> apllyFunc)
{
...
}
public T Get(NotSo<T> monad)
{
...
}
}
static class Program
{
public static TOut Make<TIn, TOut, TOutMonad>(this TIn x, TOut y, Func<TIn, TOut, TOutMonad> f)
where TOutMonad: IMonad<TOut, TOutMonad>
{
...
}
static void Main(string[] args)
{
new Easy().Make(new Simple(), (e, s) => new NotSo<Simple>());
}
}
Зачем — это уже второй вопрос
Re[10]: Есть ли вещи, которые вы прницпиально не понимаете...
Здравствуйте, include2h, Вы писали:
I>Здравствуйте, Klapaucius, Вы писали:
K>>Ну, я готов дать разъяснения, если есть конкретные вопросы.
I>Для этого нужно знать (и судя по всему весьма неплохо знать) SML, Haskell и т.д. Причем, как я понимаю, мало прочитать книжку — нужно на нем что-то написать реальное.
I>Вы можете объяснить что такое монады, используя базовую терминологию языков типа C++/C#/Java?
Ну вот смотри, в С++ есть исключения. В результате ты пишешь код, как будто их нет, зная, что выполнение автоматически прервется, если исключение будет сгенерировано. А если бы встроенных исключений не было, то пришлось бы городить лес из проверок errno после каждой строчки.
А теперь представь, что у тебя есть способ все эти ифы убрать с глаз долой. То есть код в результате будет выглядеть, как будто есть исключения, но под капотом он по-прежнему будет проверять errno. Вот такой способ — это и есть монада (одна из). Это просто способ связать вычисления, а уж как ты их хочешь связать — дело твое.
Тут тип не совсем правильный. Здесь
bind :: m t -> (t -> t) -> m t
а нужно
bind :: m t -> (t -> m r) -> m r
т.е. что-то вроде
RMonad Bind(TMonad monad, Func<T, RMonad> applyFunc);
где RMonad — некий тип R завернутый в Monad.
Re[14]: Есть ли вещи, которые вы прницпиально не понимаете...
Здравствуйте, alex_public, Вы писали:
I>>Это будет монада, но это будет отстой, потому что в более менее серьезном приложении придется понавыписывать столько операторов, что вспотеешь только перечислять их.
_>С чего бы это? Все типы не фиксированы, так что достаточно одной версии.
optional<R> operator>>(optional<T> o, optional<R> (*f)(const T&)
I>>Монады же нужны для того, что бы не надо было этим заниматься, то есть, вообще.
_>Это понятно. Не понятно другое — нафига нам вообще задаваться целью строить подобные конвейеры, если и без них всё отлично пишется. Причём код получается даже более простой и очевидный.
Проще
1 не всегда
2 только в сравнении с версией в языке, который не поддерживает монад
Скажем, optional очень хорошо помогает в больших выражениях. Я сильно сомневаюсь, что проверки каждого элемента в цепочке на null делают код простым и понятным.
Здравствуйте, Klapaucius, Вы писали:
K>Но если подумать, вариантов проблемы может быть сколько угодно. Можно возвращать перечисление простых результатов, можно либо простой результат, либо объяснение, почему он не может быть получен, можно обещание вычислить простой результат в будущем, можно результат и какое-то промежуточное состояние, можно простой результат и недообработанные данные.
Как я понял, это концепция заворачивания объектов в какие-то универсальные обертки, которые сами по себе влияют на поток выполнения (или определяют поток выполнения). Эти обертки дают оборачиваемым типам данных какие-то новые свойства, может быть даже порождают на их основе новые сущности (практически как шаблоны в С++). Отличие от шаблонов в том, что это не тупая кодогенерация новых типов путем подстановки, а некие интеллектуальные возможности прозрачной интеграции этих новых типов ВМЕСТО старых.
Если я правильно понял: Maybe — добавляет "нуллабельность" к типу, List — позволяет обрабатывать сразу много значений вместо одного, Future — вычисляет в другом потоке и обещает возвратить результат в будущем. Вероятно можно еще возвращать вместе с результатом какую-то дополнительную информацию, делать логирование и т.д. (кстати приведите еще примеры)
Теперь представим себе, что мы хотим ввести монады в простой си-подобный язык программирования.
Я пишу код, как-бы не думая о монадах. Если мне нужно написать функцию сложения двух чисел типа int, то я пишу простую функцию
int sum(int x, int y) { return x+y; }
Если мне в каком-то другом месте по смыслу программы нужно возвратить "nullable int", то я конкретно такой тип и возвращаю.
Maybe<int> foo() {/*..*/}
А монады, если я правильно понимаю, каким-то образом делают возможным прозрачную передачу типа Maybe<int> в функцию, принимающую int?
sum(foo(),100);
т.е. без всяких синтаксических ухищрений, а вот просто так взяли и передали, и на выходе вместо int получили Maybe<int> ?
Аналогично, если я передам в sum() объект List(), то я и на выходе получу List(), при этом sum() даже не узнает, что ее вызывают для целого списка чисел?
Здравствуйте, include2h, Вы писали:
I>Как я понял, это концепция заворачивания объектов в какие-то универсальные обертки, которые сами по себе влияют на поток выполнения (или определяют поток выполнения).
Вроде того. Как кто-то метко выразился, это способ переопределить оператор ";".
I>Теперь представим себе, что мы хотим ввести монады в простой си-подобный язык программирования. I>Я пишу код, как-бы не думая о монадах. Если мне нужно написать функцию сложения двух чисел типа int, то я пишу простую функцию I>
int sum(int x, int y) { return x+y; }
I>А монады, если я правильно понимаю, каким-то образом делают возможным прозрачную передачу типа Maybe<int> в функцию, принимающую int? I>
sum(foo(),100);
I>т.е. без всяких синтаксических ухищрений, а вот просто так взяли и передали, и на выходе вместо int получили Maybe<int> ? I>Аналогично, если я передам в sum() объект List(), то я и на выходе получу List(), при этом sum() даже не узнает, что ее вызывают для целого списка чисел?
Совсем прозрачно вряд ли получится, ибо во-первых это будет обманом пользователя (если я сказал, что ф-я принимает число, то разрешать передавать туда что-то другое без моего разрешения — прямое нарушение), хорошо бы все-таки в явном виде разрешить подобную перегрузку, а во-вторых это все-равно не будет работать, если разные аргументы окажутся в разных монадах.
В существующем языке Идрисе это выглядит так. Можно в явном виде сказать, что аргументы могут быть завернуты в произвольную монаду, и результат получается в ней же:
sum : (Monad m, Num a) => m a -> m a -> m a -- sum работает с любым числоподобным типов в любой монаде
sum x y =[| x + y |] -- такие скобочки называются idiom brackets и делают всю магию
Попробуем использовать ее в REPL'e:
> sum (Id 1) (Id 2)
Id 3 : Identity Integer
> sum (Just 1) (Just 2)
Just 3 : Maybe Integer
> sum [1] [2]
[3] : List Integer
> sum [1] (Just 2)
Can't unify
Maybe a
with
List a
Т.е. пока оба аргумента в одной и той же монаде, все работает нормально, и на выходе имеем то число, то Maybe число, то список чисел. Но стоит передать значения в разных монадах, как сказка заканчивается: нет никакого автоматического способа разрулить эту ситуацию. Как говорит нам теория категорий, монады не коммутируют.
Теоретически нечто подобное можно сделать и в си-подобном языке, и тогда хотя бы при одинакового типа аргументах sum будет полиморфна по используемой монаде. Но сначала придется вытащить из языка зашитую туда ранее монаду IO, т.е. сделать его чистым, а чистый си-подобный язык — то еще удовольствие.
Re[15]: Есть ли вещи, которые вы прницпиально не понимаете...
Здравствуйте, Ikemefula, Вы писали:
I>optional<R> operator>>(optional<T> o, optional<R> (*f)(const T&)
и? )
I>Проще I>1 не всегда I>2 только в сравнении с версией в языке, который не поддерживает монад
I>Скажем, optional очень хорошо помогает в больших выражениях. Я сильно сомневаюсь, что проверки каждого элемента в цепочке на null делают код простым и понятным.
Монада подобного типа встроена в большинство императивных языков — в них она называется "исключения". )))
Здравствуйте, include2h, Вы писали:
I>А монады, если я правильно понимаю, каким-то образом делают возможным прозрачную передачу типа Maybe<int> в функцию, принимающую int? I>...
I>Прокомментируйте плиз, прав я или нет.
Типа того. Только эта функция ещё обязательно должна возвращать Maybe<int> и соответственно концепция монад препятствует появлению сущностей типа Maybe<Maybe<int>>.
Re[16]: Есть ли вещи, которые вы прницпиально не понимаете...
Здравствуйте, alex_public, Вы писали:
I>>Скажем, optional очень хорошо помогает в больших выражениях. Я сильно сомневаюсь, что проверки каждого элемента в цепочке на null делают код простым и понятным.
_>Монада подобного типа встроена в большинство императивных языков — в них она называется "исключения". )))
Эта монада не исключение, а maybe
Re[17]: Есть ли вещи, которые вы прницпиально не понимаете...
Здравствуйте, Ikemefula, Вы писали:
I>Эта монада не исключение, а maybe
Да не о том речь. Вот допустим если бы в моём пример выше функции были с прототипами:
string read();
int parse(string);
void write(int);
и при этом бросали исключения для невалидных данных и т.п. При таком раскладе мы можем нарисовать гораздо более симпатичный код, чем вариант с конвейером.
Re[2]: Есть ли вещи, которые вы прницпиально не понимаете...
Здравствуйте, dimgel, Вы писали:
D>Монады. Несколько раз подкатывал, без толку.
Я, кстати, раз делал доклад на семинаре по монадам и их реализации в Haskell. Получилась даже такая небольшая популярная статья. Если интересно, могу выслать.
Re[18]: Есть ли вещи, которые вы прницпиально не понимаете...
Здравствуйте, alex_public, Вы писали:
_>Здравствуйте, Ikemefula, Вы писали:
I>>Эта монада не исключение, а maybe
_>Да не о том речь. Вот допустим если бы в моём пример выше функции были с прототипами: _>
_>и при этом бросали исключения для невалидных данных и т.п. При таком раскладе мы можем нарисовать гораздо более симпатичный код, чем вариант с конвейером.
Чем он симпатичный? Тем что все неявное скрыто от читающего? Тем что для проверки корректности кода нужно дополнительно вникать что будет если наступит конец файлы или число не может быть распарсено?
Все эти неявные выходы усложняют анализ кода как человеком, так и компьютером. Если неявные эффектны делаем явными (выражаем в типах значений), то это упрощает анализ кода очень сильно. Проще анализ — меньше ошибок — выше скорость разработки.
С другой стороны выражение всех эффектов усложняет написание кода, но тут и помогают монады.
Re[3]: Есть ли вещи, которые вы прницпиально не понимаете...
Здравствуйте, mefrill, Вы писали:
M>Я, кстати, раз делал доклад на семинаре по монадам и их реализации в Haskell. Получилась даже такая небольшая популярная статья. Если интересно, могу выслать.
Зачем нужны монады в хаскеле всем очевидно — там без них просто смерть. ))) Тут обсуждается совсем другой вопрос: зачем могут понадобиться монады в нормальных мультипарадигменных языках. Вот подобная статья была бы крайне интересной...
Re[18]: Есть ли вещи, которые вы прницпиально не понимаете...
_>и при этом бросали исключения для невалидных данных и т.п. При таком раскладе мы можем нарисовать гораздо более симпатичный код, чем вариант с конвейером.
Этот твой "симпатичный" на самом деле короткий, примерно так:
void DoAll()
Такая красота никому не нужна. Нужна возможность в одном небольшом фрагменте кода проанализировать все локальные проблемы. С исключениями ты так не сможешь сделать. Т.е. все важные эффекты кода должны быть явными.
Re[19]: Есть ли вещи, которые вы прницпиально не понимаете...
Здравствуйте, gandjustas, Вы писали:
G>Чем он симпатичный? Тем что все неявное скрыто от читающего? Тем что для проверки корректности кода нужно дополнительно вникать что будет если наступит конец файлы или число не может быть распарсено? G>Все эти неявные выходы усложняют анализ кода как человеком, так и компьютером. Если неявные эффектны делаем явными (выражаем в типах значений), то это упрощает анализ кода очень сильно. Проще анализ — меньше ошибок — выше скорость разработки. G>С другой стороны выражение всех эффектов усложняет написание кода, но тут и помогают монады.
Ну так в случае монад этот код всё равно остаётся скрытым, просто уже в самой монаде. Конечно если человек держит их все в уме, то код пишется/читается легко... Но он точно так же может держать в уме и сигнатуры используемого интерфейса api (с исключениями или без) и тогда код аналогично будет легко писаться/читаться.
Re[19]: Есть ли вещи, которые вы прницпиально не понимаете...
Здравствуйте, Ikemefula, Вы писали:
I>Этот твой "симпатичный" на самом деле короткий, примерно так:
I>
I>void DoAll()
I>
Не, там будет что-то вроде:
try{
write(parse(read()));
}catch(...){}
Если сравнивать это с
F2O(read)>>parse>>write;
и с
string s;
int v;
if(read(s)&&parse(s, v)) write(v);
То совершенно не очевидно, где код проще для понимания. На самом деле самый понятный вариант последний, но он многословный. Первые два варианта одинаково скрывают часть логики, но при этом первый использует общепринятую в императивных языках схему...
I>Такая красота никому не нужна. Нужна возможность в одном небольшом фрагменте кода проанализировать все локальные проблемы. С исключениями ты так не сможешь сделать. Т.е. все важные эффекты кода должны быть явными.
При желание можно сделать красиво и понятно любым способом (и явным кодом и монадами и исключениями и думаю ещё можно много вариантов напридумывать). ))) Вопрос в том нафига плодить лишнюю сущность (монады), если в языке и так уже есть несколько путей для реализации подобного. Мы же тут не про Хаскель, в котором других путей просто нет...
Re[20]: Есть ли вещи, которые вы прницпиально не понимаете...
_>То совершенно не очевидно, где код проще для понимания. На самом деле самый понятный вариант последний, но он многословный. Первые два варианта одинаково скрывают часть логики, но при этом первый использует общепринятую в императивных языках схему...
Если самый понятный последний вариант, то почему ты на ассемблере не пишешь ? Вот там уж точно всё понятно.
I>>Такая красота никому не нужна. Нужна возможность в одном небольшом фрагменте кода проанализировать все локальные проблемы. С исключениями ты так не сможешь сделать. Т.е. все важные эффекты кода должны быть явными.
_>При желание можно сделать красиво и понятно любым способом (и явным кодом и монадами и исключениями и думаю ещё можно много вариантов напридумывать). ))) Вопрос в том нафига плодить лишнюю сущность (монады), если в языке и так уже есть несколько путей для реализации подобного. Мы же тут не про Хаскель, в котором других путей просто нет...
Возьми что нибудь посложнее, например парсер. Хочется иметь полный контроль типов, поддержку компилятора и задавать грамматику в виде максимально близком к БНФ.
Re: Есть ли вещи, которые вы прницпиально не понимаете...
G>Или не до конца понимаете в программировании? Для меня вот например Oracle это что-то типа пятого измерения В теории какбы понятно — деревья, логарифмические алгоритмы, интерпретаторы с перкомпиляцией, кэши разные. Но как оно все вместе так хитро собрано, и почему оно такое пц быстрое, и при этом устойчивое, и как работает его оптимизатор? Вообще не представляю.
Для меня загадка — современные алгоритмы шифрования (криптографии). Мат.аппарата не хватает
На практическом уровне — public key/private key понятно, но чо там внутри — чисто магия.