Здравствуйте, AlexRK, Вы писали:
ARK>Опять же, оставив вопрос копипасты — количество кода с монадами меньше не стало.
Не стало, но код в итоге получается более предсказуемый. И до кучи получается неявный pattern matching
В языке без поддержки монад все получается очень многословно, настолько, что профита никакого не будет. Но тем не менее есть вещи, где можно получить профит с монадами и в таком языке
Здравствуйте, AlexRK, Вы писали:
ARK>Разве нельзя сделать примерно так (предположим, что в С# есть интерфейсы для чисел и классы типов):
... ARK>И все — можно в этот Add пхать что угодно, хоть число, хоть Maybe...
Если совсем правильно, то вот так
var a = b + c;
Это предельно просто — сложить два числа.
Если b и c это числа, то все просто.
Теперь фокус — b + c это значения "в будущем", то есть, результаты асинхронной операции. Связываем сейчас, в a, конечный результат получим потом.
Еще фокус — при вычислении b и c возможны ошибки. Тогда a так же будет с ошибкой. Ровно тот же подход — связываем сейчас, а конечный результат получим потом, но с ошибкой.
Далее, одно число вдруг окажется nullable, ничего не меняется — b + c
ARK>Ну и самое главное — на этом примере уменьшения количества писанины не видно...
монады нужны дл того, что бы основную логику вытащить наружу, а исключения, время, состояние упрятать внутрь. То есть, что бы сложить два числа, нужно просто сложить два числа.
реально трудно переопределить все возможные операторы, потому требуется поддержка языка.
На тех же промисах можно сделать так
var a = add(b, c)
снаружи нельзя будет сказать, что это за код, синхронный, асинхронный и тд. Выглядит совершенно одинаково.
Здравствуйте, Ikemefula, Вы писали:
I>Не стало, но код в итоге получается более предсказуемый. И до кучи получается неявный pattern matching
Да, наверное более предсказуемый, но все равно места для потенциальных ошибок остались (на ровном месте вводятся дополнительные переменные, которые не нужны).
Вот к примеру, если взять цикл "for (int i = 0; i < arr.Length; i++) { use(arr[i]; }" — здесь мы видим сразу 4 места, где можно ошибиться.
Путем замены цикла на "foreach (var item in arr) { use(item); }" мы ликвидируем все из них.
Или например, если мы это:
var
val: Integer;
begin
if condition then
val = 3;
else
val = 5;
use(val);
end
меняем на:
begin
use(if condition then 3 else 5 end);
end
тоже преимущество видно и измеряемо: убрали лишнюю переменную.
А с монадами везде как-то шило на мыло получается. Я понимаю, что без монадных костылей функциональные языки не могут записать последовательность вызовов.
Но в императивном коде... Мне понравился пример jazzer'a про убирание цепочки if-ов, когда в языке нет исключений. Но опять же, ИМХО, такие вещи должны выглядеть на call-site без специальных нотаций и вообще без каких-то признаков наличия монад.
Здравствуйте, Ikemefula, Вы писали:
I>Теперь фокус — b + c это значения "в будущем", то есть, результаты асинхронной операции. Связываем сейчас, в a, конечный результат получим потом. I>Еще фокус — при вычислении b и c возможны ошибки. Тогда a так же будет с ошибкой. Ровно тот же подход — связываем сейчас, а конечный результат получим потом, но с ошибкой. I>Далее, одно число вдруг окажется nullable, ничего не меняется — b + c
I>монады нужны дл того, что бы основную логику вытащить наружу, а исключения, время, состояние упрятать внутрь. То есть, что бы сложить два числа, нужно просто сложить два числа. I>снаружи нельзя будет сказать, что это за код, синхронный, асинхронный и тд. Выглядит совершенно одинаково.
Да, вот это правильный вариант.
Интересно, можно ли этого достичь без метапрограммирования?
Здравствуйте, AlexRK, Вы писали:
ARK>Вы взорвали мне мозг.
ARK>Я, увы (а может и не увы) Haskell не знаю (синтаксис понимаю очень поверхностно). Можно пример на императивном языке?
Здесь дело не в хаскеле, просто хаскелисты любят нагнать тумана.
Представь, ты пишешь простй линейный код, все просто, нет никаких чудес.
Теперь, представь, твой линейный код стал асинхронным а последовательность операций должна сохраниться. Опаньки — это делается через монаду.
Теперь оказалось, что операции могут фейлиться, то есть нужны исключения. Опаньки — монада, которая организует последовательное выполнение, используется совместно с монадой для исключений.
Теперь твой код, внезапно, стал особым способом оперировать состоянием. Опаньки — до кучи добавилась еще монада — состояние.
Здравствуйте, AlexRK, Вы писали:
ARK>А с монадами везде как-то шило на мыло получается. Я понимаю, что без монадных костылей функциональные языки не могут записать последовательность вызовов.
Попробуй свой цикл в примере сделать асинхронным, но сохранить последовательность.
Здравствуйте, AlexRK, Вы писали:
ARK>Здравствуйте, gandjustas, Вы писали:
G>>Понятность — очень субъективный параметр. Зависит от исключительно от предыдущего опыта писания.
ARK>Не могу согласиться. Объективно некоторые вещи простые, некоторые сложные.
В том то и дело, что это субъективно.
Это как водить машину. Если ты всю жизнь ездил, то сесть за руль даже камаза не составит большого труда. Если нет, то будет казатся очень сложным.
Люди, которые всю жизнь пишут рекурсию считают циклы сложными, те кто пишут циклы — считают сложной рекурсию.
G>>Во вторых, запишем выражение по другому: G>>
G>>from v1 in a
G>>from v2 in f(v1)
G>>select g(v1, v2)
G>>
G>>И никакие переопределения операторов не помогут разрулить.
ARK>Почему? Вот вариант без страхолюдства с select и from: ARK>
ARK> g(a, f(a));
ARK>
И в каждой функции f и g (и сотнях других) ручками выписать условие когда вернется None? Нет, спасибо.
ARK>>>Ну и самое главное — на этом примере уменьшения количества писанины не видно... G>>Странно что не видно... G>>Если ты не заметил, то для "ручного" варианта надо в каждом методе, оперирующим Maybe<T> проверять что есть значение (копипастить код).
ARK>Я заметил, что код точно так же копипастится и в двух других случаях — во втором два вызова Bind, в третьем два from-in. И в них точно так же легко ошибиться: ARK>
ARK> return from v1 in a
ARK> from v2 in a
ARK> select v1+v2;
ARK>
Вероятность опечатки от монад не зависит. А необходимость вписывать проверки очень даже завист.
ARK>Опять же, оставив вопрос копипасты — количество кода с монадами меньше не стало.
Еще раз: с монадой maybe тебе не надо в каждой функции проверять что значение есть, этот код будет один раз написан в bind.
Здравствуйте, AlexRK, Вы писали:
I>>Теперь фокус — b + c это значения "в будущем", то есть, результаты асинхронной операции. Связываем сейчас, в a, конечный результат получим потом. I>>Еще фокус — при вычислении b и c возможны ошибки. Тогда a так же будет с ошибкой. Ровно тот же подход — связываем сейчас, а конечный результат получим потом, но с ошибкой. I>>Далее, одно число вдруг окажется nullable, ничего не меняется — b + c
I>>монады нужны дл того, что бы основную логику вытащить наружу, а исключения, время, состояние упрятать внутрь. То есть, что бы сложить два числа, нужно просто сложить два числа. I>>снаружи нельзя будет сказать, что это за код, синхронный, асинхронный и тд. Выглядит совершенно одинаково.
ARK>Да, вот это правильный вариант. ARK>Интересно, можно ли этого достичь без метапрограммирования?
Нужна или поддержка монад в языке, или метапрограммирование, или, на худой конец, динамическая типизация.
Здравствуйте, AlexRK, Вы писали:
ARK>Здравствуйте, Ikemefula, Вы писали:
I>>Теперь фокус — b + c это значения "в будущем", то есть, результаты асинхронной операции. Связываем сейчас, в a, конечный результат получим потом. I>>Еще фокус — при вычислении b и c возможны ошибки. Тогда a так же будет с ошибкой. Ровно тот же подход — связываем сейчас, а конечный результат получим потом, но с ошибкой. I>>Далее, одно число вдруг окажется nullable, ничего не меняется — b + c
I>>монады нужны дл того, что бы основную логику вытащить наружу, а исключения, время, состояние упрятать внутрь. То есть, что бы сложить два числа, нужно просто сложить два числа. I>>снаружи нельзя будет сказать, что это за код, синхронный, асинхронный и тд. Выглядит совершенно одинаково.
ARK>Да, вот это правильный вариант. ARK>Интересно, можно ли этого достичь без метапрограммирования?
Ты удивишься, но для "значений в будущем" будет уже знакомый тебе код:
Здравствуйте, gandjustas, Вы писали:
G>В том то и дело, что это субъективно. G>Это как водить машину. Если ты всю жизнь ездил, то сесть за руль даже камаза не составит большого труда. Если нет, то будет казатся очень сложным. G>Люди, которые всю жизнь пишут рекурсию считают циклы сложными, те кто пишут циклы — считают сложной рекурсию.
Я думаю, что статистика уверенно ответит на этот вопрос.
Если подавляющее большинство программистов считает нечто сложным, то оно, вероятно, и есть сложное.
Если есть две или более групп примерно равного размера, значит не сложное, а субъективное.
ARK>>Почему? Вот вариант без страхолюдства с select и from: ARK>>
ARK>> g(a, f(a));
ARK>>
G>И в каждой функции f и g (и сотнях других) ручками выписать условие когда вернется None? Нет, спасибо.
Нет, ничего выписывать не надо. Этот код должен быть абсолютно эквивалентен вашему с select-from.
G>Вероятность опечатки от монад не зависит. А необходимость вписывать проверки очень даже завист.
Вероятность опечатки зависит от количества мест, где ее можно сделать. Ваши примеры с монадами не сокращают количество таких мест.
G>Еще раз: с монадой maybe тебе не надо в каждой функции проверять что значение есть, этот код будет один раз написан в bind.
В моем гипотетическом синтаксисе "g(a, f(a));" проверять тоже ничего не нужно.
Здравствуйте, Ikemefula, Вы писали:
I>Здравствуйте, AlexRK, Вы писали:
ARK>>А с монадами везде как-то шило на мыло получается. Я понимаю, что без монадных костылей функциональные языки не могут записать последовательность вызовов.
I>Попробуй свой цикл в примере сделать асинхронным, но сохранить последовательность.
На него вполне можно смотреть как на асинхронный, если arr помечен соответствующим атрибутом (т.е. не вижу необходимости менять что-то в синтаксисе).
Здравствуйте, Ikemefula, Вы писали:
ARK>>Интересно, можно ли этого достичь без метапрограммирования? I>Нужна или поддержка монад в языке, или метапрограммирование, или, на худой конец, динамическая типизация.
А как, например, может выглядеть поддержка монад в языке? Особенно императивном. Как я понимаю, в Хаскелле этим термином именуется "do-нотация", но такой вариант мне не особо нравится. А других примеров в реальных языках, наверное, и не нет нигде?
Здравствуйте, AlexRK, Вы писали:
I>>Попробуй свой цикл в примере сделать асинхронным, но сохранить последовательность.
ARK>На него вполне можно смотреть как на асинхронный, если arr помечен соответствующим атрибутом (т.е. не вижу необходимости менять что-то в синтаксисе).
А ты таки попробуй Без этого сложновато рассуждать про монады.
Здравствуйте, Ikemefula, Вы писали:
I>>>Попробуй свой цикл в примере сделать асинхронным, но сохранить последовательность. ARK>>На него вполне можно смотреть как на асинхронный, если arr помечен соответствующим атрибутом (т.е. не вижу необходимости менять что-то в синтаксисе). I>А ты таки попробуй Без этого сложновато рассуждать про монады.
Э... а что именно попробовать? Что у нас на входе, и что мы хотим на выходе?
Я так понимаю, что речь о каком-то таком цикле
Здравствуйте, AlexRK, Вы писали:
ARK>Здравствуйте, gandjustas, Вы писали:
G>>В том то и дело, что это субъективно. G>>Это как водить машину. Если ты всю жизнь ездил, то сесть за руль даже камаза не составит большого труда. Если нет, то будет казатся очень сложным. G>>Люди, которые всю жизнь пишут рекурсию считают циклы сложными, те кто пишут циклы — считают сложной рекурсию.
ARK>Я думаю, что статистика уверенно ответит на этот вопрос. ARK>Если подавляющее большинство программистов считает нечто сложным, то оно, вероятно, и есть сложное. ARK>Если есть две или более групп примерно равного размера, значит не сложное, а субъективное.
В свое время большинство людей верила, что земпля плоская и стоит на трех китах
ARK>>>Почему? Вот вариант без страхолюдства с select и from: ARK>>>
ARK>>> g(a, f(a));
ARK>>>
G>>И в каждой функции f и g (и сотнях других) ручками выписать условие когда вернется None? Нет, спасибо.
ARK>Нет, ничего выписывать не надо. Этот код должен быть абсолютно эквивалентен вашему с select-from.
Каким образом? Maybe<T> может иметь значение Some(x) и None, при каждой операции надо проверять что внутри maybe. Код проверки надо скопировать во все фукнции, работающие с maybe. Сам он там не появился.
Монадический подход — завернуть эту проверку в bind и использовать обычные функции + do-нотацию (linq).
G>>Вероятность опечатки от монад не зависит. А необходимость вписывать проверки очень даже завист. ARK>Вероятность опечатки зависит от количества мест, где ее можно сделать. Ваши примеры с монадами не сокращают количество таких мест.
Сокращает до одного — функции bind.
G>>Еще раз: с монадой maybe тебе не надо в каждой функции проверять что значение есть, этот код будет один раз написан в bind. ARK>В моем гипотетическом синтаксисе "g(a, f(a));" проверять тоже ничего не нужно.
Прости, но давай реальность рассматривать, а не гипотетические языки.
Ибо в моем гипотетическом мире программы пишутся сами, а я только получаю деньги.
Здравствуйте, gandjustas, Вы писали:
ARK>>Вероятность опечатки зависит от количества мест, где ее можно сделать. Ваши примеры с монадами не сокращают количество таких мест. G>Сокращает до одного — функции bind.
У bind есть лишние с точки зрения задачи параметры (v1 и v2).
ARK>>В моем гипотетическом синтаксисе "g(a, f(a));" проверять тоже ничего не нужно. G> G>Прости, но давай реальность рассматривать, а не гипотетические языки.
Ну я изначально имел в виду, что этот синтаксис гипотетический. То, что в C# так написать нельзя, я в курсе. Просто текущий синтаксис явно избыточен.
Кстати, думаю, что и в реальности есть языки, где можно так писать — какой-нибудь D.