Re[10]: Что должен возвращать if?
От: kekekeks  
Дата: 21.10.14 08:23
Оценка:
Здравствуйте, DarkEld3r, Вы писали:

DE>Причём вы пишете на немерле, вот и думай, что выбирать или знакомый синтаксис или "реально используемый" диалект языка.


Тандему VB/C# это не очень мешает. Хотя по сути это один язык с двумя сильно разными синтаксисами.
Re[9]: Что должен возвращать if?
От: AndrewVK Россия http://blogs.rsdn.org/avk
Дата: 21.10.14 10:45
Оценка: +1
Здравствуйте, VladD2, Вы писали:

VD>По сему менять что–то в немерле считаю не целесообразным. Нужно допиливать нитру и делать на ее основе 2 или три языка поддерживающие главные концепции, но имеющие разный синтаксис.


Вас на один язык не хватает. Про то что нужно компилятор переписывать я с самого начала твоего увлечения Немерле слышу. А тут ещ 2 языка...
Вобщем, вангую еще раз — через пять лет активно что то писать на Немерле будет не больше пары десятков человек.
... << RSDN@Home 1.0.0 alpha 5 rev. 0 on Windows 8 6.2.9200.0>>
AVK Blog
Re[8]: Что должен возвращать if?
От: Evgeny.Panasyuk Россия  
Дата: 21.10.14 11:03
Оценка:
Здравствуйте, gbear, Вы писали:

G>А как вы собираетесь это потом использовать?! Повторюсь, смысл вычисления в maybe — возможность понять что "что-то пошло не так". Использование неявного приведения Just[T] к T (которое, кстати, вполне возможно) лишает нас этой возможности. Если нет неявного приведения,


Да, в этом случае его быть не должно.

G>то "просто так" использовать результат вычисления if-без-else в тех же местах, где мы можем использовать результат вычисления if-с-else не получится.

G>Вот и вся проблема.

И там и там Variant. В том что набор типов Variant'а будет отличаться при комментировании ветки else — не вижу проблемы

EP>>Почему же? Для обобщённого кода как раз и не лишено — вне зависимости от того, значения каких типов возвращают ветки if-else, всё выражение вычислится в Variant.

G>Ну причем тут variant-то?! Речь идет о том, что нет смысла вычислять if-с-else в maybe.

Variant — своего рода обобщение Maybe. Вычислять if-с-else в maybe — действительно не имеет смысла.

EP>>В случае когда у нас Variant<T>, полноценный функторный fmap не нужен, точнее можно без него. Достаточно T unbox(Variant<T> x);, или короткого оператора.

G>По-ходу, как-то я не ясно выразился. Я вам про одно — вычисление if-без-else в maybe, а вы мне про другое — вычисление if-c-else в variant
G>Ладно, поговорим за variant...
G>А зачем вам какой-то явный unbox для variant? Вполне можно обойтись и неявным приведением к.

А где именно оно будет происходить? if-else будет сразу иметь этот тип? Или при присвоении к значению типа T?

G>Смотрите... вот получили мы какой-то variant[string, int, ?DateTime], условно. Пусть даже у него есть какой-то unbox.


А для него не будет unbox'а — unbox это только для variant[T], то есть для тех случаев, когда обе ветки if-else имеют одинаковый тип результата, и variant внутри имеет значение только одного определённого типа.

G>Нормально обработать его можно только через PM. Вы же, надеюсь, не собираетесь прогонять его через очередной if? Об этом и речь.


Можно и через if, а можно и спрятать этот if/условный переход в библиотеку, так как это сделано в boost::variant.
Обычная работа с variant'ом. В чём проблема-то?

G>>>Вычисление if-с-else в variant, само по себе (без попытки добиться "симметрии") может иметь смысл. Но только в том случае, когда PM и использование variant уйдут на ступень выше, так сказать.

EP>>То есть основная проблема заключается в каких-то пробелах в Nemerle с Varaint'ом как таковым, а не в самой идеи if-else -> Variant?
G>Nemerle тут вообще не причем Недостаточно вычислить if в variant[A, B, C]. Нужно еще как-то "узнать", что конкретно этот variant сейчас в себе несет... A, B или C. И его нужно будет либо дополнительно проверять...

Точно также как и в случае с виртуальным вызовом метода результата

G>Но просто так (без PM) использовать не получится. Хоть в Nemerle, хоть еще где.


Во-первых, в Nemerle же есть PM — в чём проблема?
Во-вторых — я же приводил уже примеры использования variant'а в C++ — нужен конкретный код?
Re[10]: Что должен возвращать if?
От: VladD2 Российская Империя www.nemerle.org
Дата: 21.10.14 21:46
Оценка:
Здравствуйте, AndrewVK, Вы писали:


AVK>Вас на один язык не хватает. Про то что нужно компилятор переписывать я с самого начала твоего увлечения Немерле слышу. А тут ещ 2 языка...


Это почти ничего не будет стоить. Основные усилия идут на Нитру.

AVK>Вобщем, вангую еще раз — через пять лет активно что то писать на Немерле будет не больше пары десятков человек.


Ну, уже лучше чем прошлое вангование про "сдохнет".

Но ты сам себе противоречишь. То говоришь о проблеме переучивания, то говоришь, что делать суперсет шарпа нецелесообразно.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[10]: Что должен возвращать if?
От: VladD2 Российская Империя www.nemerle.org
Дата: 21.10.14 22:43
Оценка:
Здравствуйте, DarkEld3r, Вы писали:

DE>Может создаться впечатление, что вы распыляетесь и не можете сами определиться.


А мы и не можем. За немерловый синаксис голосует душа и опыт. За шарповский — расчет.

DE>Или вот представим, что человек знает шарп, но услышал про ваш язык и заинтересовался. Изначально интерес может быть праздным — это нормально. Желательно на этом этапе увлечь. А у человека возникнет выбор — или брать "более знакомый" "шарп++" или немерле. Причём вы пишете на немерле, вот и думай, что выбирать или знакомый синтаксис или "реально используемый" диалект языка.


Ну, дык если его подвигнеь на знакомство знакомость базы — это и будет то что нужно нам.

Тут надо понимать, что мы не будем делать два разных мира. Это будут два разных синтаксиса для одной модели (т.е., по сути, одного язака). Оба синтаксиса можно будет использовать в рамках одного проекта. Более того можно будет и обычный шарп в такой проект включать.

DE>Если что, то это я по себе сужу. Разумеется, могу быть не обьективным. Тем не менее, на мой взгляд, лучше всё-таки сделать один немерле2.


Пока что расклад таков — ты думаешь, оцениваешь, но не используешь/изучаешь Немерл. Стало быть, если знакомость базы тебя сподвигнет присоедениться к комьюнити (без маркетингового обмана), то это и будет разрывам замкнутого круга.

А далее ты сможешь освоить сначала "++", а потом и более горманичный синтаксис. Ну, а не освоишь — тоже не беда. Ты станешь частью комьюнити и сможешь приносить ему пользу создавая новые DSL–и или внося вклад в развитие проекта.

Ну, а если в итоге окажется, что синтаксис немерла окажется не востребованным, значит это грусный парадокс с которым придется смириться.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[9]: Что должен возвращать if?
От: gbear Россия  
Дата: 22.10.14 03:51
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>И там и там Variant. В том что набор типов Variant'а будет отличаться при комментировании ветки else — не вижу проблемы


Еще раз. Variant — _не_ maybe. Даже близко не maybe. Даже не аналог. Конкретная реализация maybe вполне _может_ использовать variant в качестве "кишок" — это да. Но, только и всего.

EP>Variant — своего рода обобщение Maybe. Вычислять if-с-else в maybe — действительно не имеет смысла.


Уф. Сдается мне, у вас какое-то своеобразное представление о maybe. Давайте на пальцах...

Представим себе, что у нас есть у нас ф-ция f(int) -> maybe[int]. В какой, по вашему мнению, тип должен быть вычислен if(x > 0) f(x)?
Мне, вот, очевидно — в тот же самый maybe[int]. Надеюсь, что и вам тоже. И если это так, то объясните мне, как вы собираетесь провернуть такой "фокус" на "читых" variant'ах?

G>>А зачем вам какой-то явный unbox для variant? Вполне можно обойтись и неявным приведением к.

EP>А где именно оно будет происходить? if-else будет сразу иметь этот тип? Или при присвоении к значению типа T?

Почему только при присвоении?! Везде, где "по смыслу" должно быть T.

EP>А для него не будет unbox'а — unbox это только для variant[T], то есть для тех случаев, когда обе ветки if-else имеют одинаковый тип результата, и variant внутри имеет значение только одного определённого типа.


Вот уж в этом-то случае, явный unbox смысла не имеет вообще.

G>>Нормально обработать его можно только через PM. Вы же, надеюсь, не собираетесь прогонять его через очередной if? Об этом и речь.

EP>Можно и через if, а можно и спрятать этот if/условный переход в библиотеку, так как это сделано в boost::variant.
EP>Обычная работа с variant'ом. В чём проблема-то?

Уж вот я не знаю как для вас. А для меня то, что я увидел по вашей сслыке на boost — является всем чем угодно, но не "обычной работой с variant'ом".

В нашем случае использовать if не получится... конечно, если вы не предлагаете "нафантазировать" для этого случая еще один — специальный — "if". Ну, который не будет вычисляться в variant .
А вариант с двойной диспетчеризацией — еще хуже. Надеюсь, вы сами это понимаете.

G>>Nemerle тут вообще не причем Недостаточно вычислить if в variant[A, B, C]. Нужно еще как-то "узнать", что конкретно этот variant сейчас в себе несет... A, B или C. И его нужно будет либо дополнительно проверять...

EP>Точно также как и в случае с виртуальным вызовом метода результата

?! Вы вообще о чем? Как определить этот "метод"? Смысл использования variant'а — по вашей же логике — "впихнуть невпихуемое". Если мы подразумеваем общий интерфейс, то зачем variant-то?!

G>>Но просто так (без PM) использовать не получится. Хоть в Nemerle, хоть еще где.

EP>Во-первых, в Nemerle же есть PM — в чём проблема?

Проблему можно выразить так: Если результат if можно обработать только через PM, то зачем нам использовать if? Почему сразу не использовать PM?!

Напоминаю, в сабже if — это "сахар" для PM.

EP>Во-вторых — я же приводил уже примеры использования variant'а в C++ — нужен конкретный код?


Не нужен. Яж вам говорю... чудес не бывает

Одно дело, какой-нибудь условный:

match (if (x > 0) getA() else getB())
{
  | y is A => handleA(y.PropertyOfA)
  | y is B => handleB(y.MethodOfB())
}


А другое — предлагаемые вами "танцы на граблях" (с)

---
С уважением, Константин Сиваков
Re[11]: Что должен возвращать if?
От: DarkEld3r  
Дата: 22.10.14 08:27
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>Пока что расклад таков — ты думаешь, оцениваешь, но не используешь/изучаешь Немерл. Стало быть, если знакомость базы тебя сподвигнет присоедениться к комьюнити (без маркетингового обмана), то это и будет разрывам замкнутого круга.

Не использую/изучаю по несколько другим причинам — мне "нужен" (действительно нужен или это "предрассудки" — не важно) нативный язык. Поэтому изучаю Pаст.

По моему, освоить непривычный синтаксис не особо сложно. Скажем, после сишного опыта с лиспом разобраться вполне удалось. Было бы желание. Более того, непривычность может подталкивать писать "правильно". В этом плане мне нравится Nemerle.Imperative — при желании можно "как раньше", но по умолчанию предполагается использовать функциональный стиль.

Изучал Racket когда-то из интереса. Подкупало то, что у них есть типизированный диалект. Казалось бы, всё здорово, но постоянно вылезали грабли, что разные возможности в typed racket не поддерживаются. В итоге забил именно поэтому.

Конечно, может просто проецирую свой негативный опыт и у вас таких проблем не возникнет...
Re[11]: Что должен возвращать if?
От: DarkEld3r  
Дата: 22.10.14 08:28
Оценка:
Здравствуйте, kekekeks, Вы писали:

K>Тандему VB/C# это не очень мешает. Хотя по сути это один язык с двумя сильно разными синтаксисами.

Возможно, я не прав, но никогда VB серьёзно не воспринимал. Tак понимаю, что некая база пользователей языка была, ну и её попытались сохранить перетащив язык на дотнет. Мне кажется, что это весьма консервативные люди, которые так и продолжат писать "на ВБ".
Re[10]: Что должен возвращать if?
От: Evgeny.Panasyuk Россия  
Дата: 22.10.14 11:20
Оценка:
Здравствуйте, gbear, Вы писали:

G>Представим себе, что у нас есть у нас ф-ция f(int) -> maybe[int]. В какой, по вашему мнению, тип должен быть вычислен if(x > 0) f(x)?

G>Мне, вот, очевидно — в тот же самый maybe[int]. Надеюсь, что и вам тоже. И если это так, то объясните мне, как вы собираетесь провернуть такой "фокус" на "читых" variant'ах?

Явно или автоматически: Variant<Variant<T, void>, void> -> Variant<T, void>

G>>>А зачем вам какой-то явный unbox для variant? Вполне можно обойтись и неявным приведением к.

EP>>А где именно оно будет происходить? if-else будет сразу иметь этот тип? Или при присвоении к значению типа T?
G>Почему только при присвоении?! Везде, где "по смыслу" должно быть T.

Вот тут:
auto foo()
{
    return (if(c) T{1} else T{2});
}

какой должен быть тип результата у foo?

EP>>А для него не будет unbox'а — unbox это только для variant[T], то есть для тех случаев, когда обе ветки if-else имеют одинаковый тип результата, и variant внутри имеет значение только одного определённого типа.

G>Вот уж в этом-то случае, явный unbox смысла не имеет вообще.

Ещё раз (третий?), речь об обобщённом коде:
template<typename T, typename U>
void bar()
{
    auto res = if(c) T{} else U{};
    // use res as variant
}

Если делать здесь неявное преобразование variant[X] в X (в случае когда T == U), то этот код поломается, либо ему придётся вставлять дополнительные проверки.


G>В нашем случае использовать if не получится...


Почему?

G>конечно, если вы не предлагаете "нафантазировать" для этого случая еще один — специальный — "if". Ну, который не будет вычисляться в variant .


Зачем?

G>А вариант с двойной диспетчеризацией — еще хуже. Надеюсь, вы сами это понимаете.


Откуда двойная диспетчеризация-то?

G>>>Nemerle тут вообще не причем Недостаточно вычислить if в variant[A, B, C]. Нужно еще как-то "узнать", что конкретно этот variant сейчас в себе несет... A, B или C. И его нужно будет либо дополнительно проверять...

EP>>Точно также как и в случае с виртуальным вызовом метода результата
G>?! Вы вообще о чем? Как определить этот "метод"? Смысл использования variant'а — по вашей же логике — "впихнуть невпихуемое". Если мы подразумеваем общий интерфейс, то зачем variant-то?!

Я уже приводил пример, придётся скопировать:
Variant<Widget, Gadget> x, y;
// ...
apply(x, y, [](auto &x, auto &y)
{
    draw(x);
    draw(y);
    collide(x, y);
});

Здесь нет общего предка у Widget и Gadget (да и вообще у них нет ни одного метода), а интерфейс использования в данном случае у них одинаковый

G>>>Но просто так (без PM) использовать не получится. Хоть в Nemerle, хоть еще где.

EP>>Во-первых, в Nemerle же есть PM — в чём проблема?
G>Проблему можно выразить так: Если результат if можно обработать только через PM, то зачем нам использовать if? Почему сразу не использовать PM?!

Во-первых, не только через PM.
Во-вторых, претензия не понятна — в коде удобен if, получили из него variant, передали куда-то дальше, там обработали хоть PM'ом хоть ещё чем. В чём проблема-то?

G>Напоминаю, в сабже if — это "сахар" для PM.


И что? Это мешает построить if возвращающий variant?

EP>>Во-вторых — я же приводил уже примеры использования variant'а в C++ — нужен конкретный код?

G>Не нужен. Яж вам говорю... чудес не бывает
G>Одно дело, какой-нибудь условный:

G>
G>match (if (x > 0) getA() else getB())
G>{
G>  | y is A => handleA(y.PropertyOfA)
G>  | y is B => handleB(y.MethodOfB())
G>}
G>

G>А другое — предлагаемые вами "танцы на граблях" (с)

Чем код выше, принципиально отличается от следующего:
MATCH( IF(x>0) { return getA(); } ELSE { return getB(); } )
    CASE(y IS A) { handleA(y.PropertyOfA); }
    CASE(y IS B) { handleB(y.MethodOfB()); }
;

Re[11]: Что должен возвращать if?
От: gbear Россия  
Дата: 22.10.14 16:38
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>Явно или автоматически: Variant<Variant<T, void>, void> -> Variant<T, void>


А явно это как? Особенно интересно, как это "явно" будет выглядеть в обобщенном коде.
А если автоматически, то получается тип Variant<Variant<T, void>, void> существовать не может? Я вас правильно понимаю?

G>>Почему только при присвоении?! Везде, где "по смыслу" должно быть T.


EP>Вот тут:

EP>
EP>auto foo()
EP>{
EP>    return (if(c) T{1} else T{2});
EP>}
EP>

EP>какой должен быть тип результата у foo?

Очевидно — variant.

EP>Ещё раз (третий?), речь об обобщённом коде:

EP>
EP>template<typename T, typename U>
EP>void bar()
EP>{
EP>    auto res = if(c) T{} else U{};
EP>    // use res as variant
EP>}
EP>

EP>Если делать здесь неявное преобразование variant[X] в X (в случае когда T == U), то этот код поломается, либо ему придётся вставлять дополнительные проверки.

Зачем здесь-то делать? Хотите работать с res как c variant'ом — ктож вам запретит Но если у вас есть, например, какая-нибудь ф-ция от X, то, согласитесь, удобней "уметь" в неё передавать variant[X] "as is", а не дергать какие-то unbox'ы.

G>>В нашем случае использовать if не получится...

EP>Почему?

?! Очевидно, потому что if вернет нам новый variant. С которым "нужно что-то делать"

G>>конечно, если вы не предлагаете "нафантазировать" для этого случая еще один — специальный — "if". Ну, который не будет вычисляться в variant .


EP>Зачем?


?! Вы серьезно не понимаете? ?! Очевидно, потому что "нормальный" if вернет нам новый variant. С которым "нужно что-то делать"


G>>А вариант с двойной диспетчеризацией — еще хуже. Надеюсь, вы сами это понимаете.


EP>Откуда двойная диспетчеризация-то?


boost::variant< int, std::string > v;

...

class times_two_visitor
    : public boost::static_visitor<>
{
public:

    void operator()(int & i) const
    {
        i *= 2;
    }

    void operator()(std::string & str) const
    {
        str += str;
    }

};

...

boost::apply_visitor( times_two_visitor(), v );


Это пример из boost, в который вы мне тыкали, как в пример. И таки да... это называется, двойная диспетчеризация.

G>>?! Вы вообще о чем? Как определить этот "метод"? Смысл использования variant'а — по вашей же логике — "впихнуть невпихуемое". Если мы подразумеваем общий интерфейс, то зачем variant-то?!

EP>Я уже приводил пример, придётся скопировать:
EP>
EP>Variant<Widget, Gadget> x, y;
EP>// ...
EP>apply(x, y, [](auto &x, auto &y)
EP>{
EP>    draw(x);
EP>    draw(y);
EP>    collide(x, y);
EP>});
EP>

EP>Здесь нет общего предка у Widget и Gadget (да и вообще у них нет ни одного метода), а интерфейс использования в данном случае у них одинаковый

Я где-то говорил за "общего предка"?! Процитирую себя

Если мы подразумеваем общий интерфейс, то зачем variant-то?!


Что мешает-то на лету "собрать" нужный тип (в вашем примере, это агрегат Widget и Gadget), реализующий _нужный_ интерфейс?!

G>>Проблему можно выразить так: Если результат if можно обработать только через PM, то зачем нам использовать if? Почему сразу не использовать PM?!

EP>Во-первых, не только через PM.

Возвращаемся к нашим баранам. Таки через visitor'ы предлагаете? Повторюсь — обработка через if даст вам на выходе опять variant. Т.е. получаем "один раз variant — всегда variant". Оно действительно надо?

EP>Во-вторых, претензия не понятна — в коде удобен if, получили из него variant, передали куда-то дальше, там обработали хоть PM'ом хоть ещё чем. В чём проблема-то?


"Хоть PM'ом" — означает "только PM'ом".

G>>Напоминаю, в сабже if — это "сахар" для PM.

EP>И что? Это мешает построить if возвращающий variant?

Да ничего не мешает. Вопрос не в этом. Вопрос в том, чего вы этим хотите добиться? Пресловутой "симметрии" с if-без-else, как вроде бы выяснили, добиться таким образом не получится. Так в чем смысл-то?

EP>>>Во-вторых — я же приводил уже примеры использования variant'а в C++ — нужен конкретный код?

G>>Не нужен. Яж вам говорю... чудес не бывает
G>>Одно дело, какой-нибудь условный:

G>>
G>>match (if (x > 0) getA() else getB())
G>>{
G>>  | y is A => handleA(y.PropertyOfA)
G>>  | y is B => handleB(y.MethodOfB())
G>>}
G>>

G>>А другое — предлагаемые вами "танцы на граблях" (с)

EP>Чем код выше, принципиально отличается от следующего:

EP>
EP>MATCH( IF(x>0) { return getA(); } ELSE { return getB(); } )
EP>    CASE(y IS A) { handleA(y.PropertyOfA); }
EP>    CASE(y IS B) { handleB(y.MethodOfB()); }
EP>;
EP>

EP>

Принципиально — ничем. Только какое он имеет отношение к?

---
С уважением, Константин Сиваков
Re[12]: Что должен возвращать if?
От: Evgeny.Panasyuk Россия  
Дата: 22.10.14 18:30
Оценка:
Здравствуйте, gbear, Вы писали:

EP>>Явно или автоматически: Variant<Variant<T, void>, void> -> Variant<T, void>

G>А явно это как? Особенно интересно, как это "явно" будет выглядеть в обобщенном коде.

Например simplify(v) или какой-нибудь унарный оператор ^^ v

G>А если автоматически, то получается тип Variant<Variant<T, void>, void> существовать не может? Я вас правильно понимаю?


Хм, наверное да. Точнее существовать может, но до первого использования.

G>>>Почему только при присвоении?! Везде, где "по смыслу" должно быть T.

EP>>Вот тут:
EP>>
EP>>auto foo()
EP>>{
EP>>    return (if(c) T{1} else T{2});
EP>>}
EP>>

EP>>какой должен быть тип результата у foo?
G>Очевидно — variant.

Variant[T]? А как же:

G>>>Почему только при присвоении?! Везде, где "по смыслу" должно быть T.

Что имелось в виду? Протягивать Variant[T] до T? А если пользователь просто захочет позвать метод T?

G>Хотите работать с res как c variant'ом — ктож вам запретит Но если у вас есть, например, какая-нибудь ф-ция от X, то, согласитесь, удобней "уметь" в неё передавать variant[X] "as is", а не дергать какие-то unbox'ы.


Можно и так — автоматически/неявно.

G>>>В нашем случае использовать if не получится...

EP>>Почему?
G>?! Очевидно, потому что if вернет нам новый variant. С которым "нужно что-то делать"

Когда из if возвращается указатель на какую-нибудь абстрактную базу, при работе с ней придётся точно также выяснять — что же там внутри, и какой именно метод вызывать. Выбор-то никуда не исчезает

G>>>конечно, если вы не предлагаете "нафантазировать" для этого случая еще один — специальный — "if". Ну, который не будет вычисляться в variant .

EP>>Зачем?
G>?! Вы серьезно не понимаете? ?! Очевидно, потому что "нормальный" if вернет нам новый variant. С которым "нужно что-то делать"

А сейчас возвращается общая база, с которой тоже "нужно что-то делать".

G>Это пример из boost, в который вы мне тыкали, как в пример. И таки да... это называется, двойная диспетчеризация.


Там одинарная диспетчеризация, а не двойная

G>>>?! Вы вообще о чем? Как определить этот "метод"? Смысл использования variant'а — по вашей же логике — "впихнуть невпихуемое". Если мы подразумеваем общий интерфейс, то зачем variant-то?!

EP>>Я уже приводил пример, придётся скопировать:
EP>>
EP>>...
EP>>

EP>>Здесь нет общего предка у Widget и Gadget (да и вообще у них нет ни одного метода), а интерфейс использования в данном случае у них одинаковый
G>Я где-то говорил за "общего предка"?! Процитирую себя

Если мы подразумеваем общий интерфейс, то зачем variant-то?!


Тогда покажите как этот пример (где у разных типов общий интерфейс) переделать без variant'а и без общего предка.

G>Что мешает-то на лету "собрать" нужный тип (в вашем примере, это агрегат Widget и Gadget), реализующий _нужный_ интерфейс?!


Пример сборки нужного типа в студию. Мне кажется это будет сложнее чем variant.

G>>>Проблему можно выразить так: Если результат if можно обработать только через PM, то зачем нам использовать if? Почему сразу не использовать PM?!

EP>>Во-первых, не только через PM.
G>Возвращаемся к нашим баранам. Таки через visitor'ы предлагаете?

Один из вариантов. Вот только visitor может иметь один полиморфный метод, а может много перегрузок — а-ля PM, а может и то и то.

G>Повторюсь — обработка через if даст вам на выходе опять variant. Т.е. получаем "один раз variant — всегда variant". Оно действительно надо?


С общим предком тоже самое. Если же обработать всё что относится к этой ветке, внутри неё, и наружу никогда не выдавать полиморфный тип (будь то variant или абстрактная база), то мы очень быстро получим комбинаторный взрыв.


G>>>Напоминаю, в сабже if — это "сахар" для PM.

EP>>И что? Это мешает построить if возвращающий variant?
G>Да ничего не мешает. Вопрос не в этом. Вопрос в том, чего вы этим хотите добиться? Пресловутой "симметрии" с if-без-else, как вроде бы выяснили, добиться таким образом не получится. Так в чем смысл-то?

Как это не получится, если выяснили что как раз получается

EP>>Чем код выше, принципиально отличается от следующего:

EP>>
EP>>MATCH( IF(x>0) { return getA(); } ELSE { return getB(); } )
EP>>    CASE(y IS A) { handleA(y.PropertyOfA); }
EP>>    CASE(y IS B) { handleB(y.MethodOfB()); }
EP>>;
EP>>

EP>>
G>Принципиально — ничем. Только какое он имеет отношение к?

Тем что такой синтаксис можно получить на базе boost::variant
Отредактировано 22.10.2014 18:33 Evgeny.Panasyuk . Предыдущая версия . Еще …
Отредактировано 22.10.2014 18:32 Evgeny.Panasyuk . Предыдущая версия .
Re[13]: Что должен возвращать if?
От: gbear Россия  
Дата: 23.10.14 06:49
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

G>>А явно это как? Особенно интересно, как это "явно" будет выглядеть в обобщенном коде.

EP>Например simplify(v) или какой-нибудь унарный оператор ^^ v

Да я не про это. Кто будет "дергать за веревочку"? И главное — как он будет определять нужно ли "дергать"?

Есть foo(x) -> void и bar(x) -> variant[int, void].

if(x > 0) bar(x) else foo(x)


Нужно "дергать"?

if(x > 0) bar(x)


А теперь?

if(x > 0)
  if(x > 10) x else foo(x)


А если так?

G>>А если автоматически, то получается тип Variant<Variant<T, void>, void> существовать не может? Я вас правильно понимаю?

EP>Хм, наверное да. Точнее существовать может, но до первого использования.

Плохо это. Потеряем возможность поймать "go wrong". Variant<Variant<T, void>, void> вполне может означать — что все ок... ну просто, например, вложеный if-c-else вычислился в void.
Привели к Variant<T, void> — и если "внутрях у неё" void — будем думать, что до вложенного if дело вообще не дошло.

Вывод, нужен таки maybe, а не суррогаты

G>>>>В нашем случае использовать if не получится...

EP>>>Почему?
G>>?! Очевидно, потому что if вернет нам новый variant. С которым "нужно что-то делать"
EP>Когда из if возвращается указатель на какую-нибудь абстрактную базу, при работе с ней придётся точно также выяснять — что же там внутри, и какой именно метод вызывать. Выбор-то никуда не исчезает

def a = if(x > 0) "a" else 1; // "абстракная база" в виде какого-нибудь IConvertable  
def b = if(a is string) stringToX(a) else intToX(a); // Вполне конкретный X


В вашем случае: b — это в лучшем случае variant[X]. Что дальше? Можно и по сложней примеры нарисовать — например, когда "база" таки нужна. Но, в вашем варианте получим очередной variant[T, U] .

G>>Это пример из boost, в который вы мне тыкали, как в пример. И таки да... это называется, двойная диспетчеризация.


EP>Там одинарная диспетчеризация, а не двойная


Да же так

template <typename Visitor, typename Visitable>
inline
    BOOST_VARIANT_AUX_APPLY_VISITOR_NON_CONST_RESULT_TYPE(Visitor)
apply_visitor(Visitor& visitor, Visitable& visitable)
{
    return visitable.apply_visitor(visitor);
}

...

template <typename Visitor, typename VoidPtrCV, typename T>
inline
    BOOST_VARIANT_AUX_GENERIC_RESULT_TYPE(typename Visitor::result_type)
visitation_impl_invoke_impl(
      int, Visitor& visitor, VoidPtrCV storage, T*
    , mpl::true_// never_uses_backup
    )
{
    return visitor.internal_visit(
          cast_storage<T>(storage), 1L
        );
}


Т.е. вот это все мне мерещится?!

Понимаете, то, что в данном случае диспетчеризация — скорее всего... этожплюсы, в конце концов — статическая (не виртуальная, если хотите) — ничего не меняет.

G>>Что мешает-то на лету "собрать" нужный тип (в вашем примере, это агрегат Widget и Gadget), реализующий _нужный_ интерфейс?!

EP>Пример сборки нужного типа в студию. Мне кажется это будет сложнее чем variant.

А в чем проблема? Я такой тип вам и в C# соберу "налету" — только в run-time. В чем проблемы то?! Нам же нужен только агригат (контейней, если хотите), который делегирует вызовы конкретного интерфейса, конкретному экземпляру.
В Nemerle — такой же фокус можно провернуть в compile-time. А сложность тут вообще не причем... этот код пишется ровно один раз.

EP>Тем что такой синтаксис можно получить на базе boost::variant


Синтаксис-то тут причем?! Скажите лучше во что это вычислится? В variant?

---
С уважением, Константин Сиваков
Re[14]: Что должен возвращать if?
От: Evgeny.Panasyuk Россия  
Дата: 23.10.14 08:15
Оценка:
Здравствуйте, gbear, Вы писали:

G>>>А явно это как? Особенно интересно, как это "явно" будет выглядеть в обобщенном коде.

EP>>Например simplify(v) или какой-нибудь унарный оператор ^^ v
G>Да я не про это. Кто будет "дергать за веревочку"?

Программист вестимо, а кто ещё если это "явное" упрощение?

G>И главное — как он будет определять нужно ли "дергать"?


Самому или с помощью компилятора. Например сейчас же компилятор ругается на вывод Object'а?

G>Есть foo(x) -> void и bar(x) -> variant[int, void].

G>
G>if(x > 0) bar(x) else foo(x)
G>

G>Нужно "дергать"?

Если неявного упрощения нет — то да, можно.

G>
G>if(x > 0) bar(x)
G>

G>А теперь?

И здесь можно.

G>
G>if(x > 0)
G>  if(x > 10) x else foo(x)
G>

G>А если так?

И здесь.

G>>>А если автоматически, то получается тип Variant<Variant<T, void>, void> существовать не может? Я вас правильно понимаю?

EP>>Хм, наверное да. Точнее существовать может, но до первого использования.
G>Плохо это. Потеряем возможность поймать "go wrong". Variant<Variant<T, void>, void> вполне может означать — что все ок... ну просто, например, вложеный if-c-else вычислился в void.

При неявном упрощении, поймать "go wrong" можно во внутренней вложенности.

G>Вывод, нужен таки maybe, а не суррогаты


Вывод: необходимо определится нужно ли неявное упрощение или нет. Как и всегда, у explicit и implicit есть свои плюс и минусы.
При этом у нас всегда есть возможность вернуться к текущему поведению Nemerle позвав специальную функцию/оператор.

G>
G>def a = if(x > 0) "a" else 1; // "абстракная база" в виде какого-нибудь IConvertable  
G>def b = if(a is string) stringToX(a) else intToX(a); // Вполне конкретный X
G>

G>В вашем случае: b — это в лучшем случае variant[X]. Что дальше?

При отсутствии неявного упрощения — simplifiy(b)

G>Можно и по сложней примеры нарисовать — например, когда "база" таки нужна. Но, в вашем варианте получим очередной variant[T, U] .


А в этом случае deduce_base(b)
Я уже неоднократно говорил, что вернуться от variant'а к текущему поведению всегда возможно через спец вызов, а вот наоборот уже нет.

G>Понимаете, то, что в данном случае диспетчеризация — скорее всего... этожплюсы, в конце концов — статическая (не виртуальная, если хотите) — ничего не меняет.


Там, по сути, внутри union + tag, по тэгу делается один switch, в ветках case делается cast к текущему типу и вызов visitor'а.
Если хотите называть простой вызов конкретной функции, у объекта конкретного типа, где не делается никакой выбор, даже compile time (хотя бы что-то типа category dispatch как в std::distance) dispatch'ем — то тогда вам придётся и все методы типа std::vector<T>::push_back называть single dispatch'ем.
Да, бывает compile time диспетчеризация, как в std::distance, перегрузках, специализациях и т.п. Но тут-то ничего этого нет.

G>>>Что мешает-то на лету "собрать" нужный тип (в вашем примере, это агрегат Widget и Gadget), реализующий _нужный_ интерфейс?!

EP>>Пример сборки нужного типа в студию. Мне кажется это будет сложнее чем variant.
G>А в чем проблема? Я такой тип вам и в C# соберу "налету" — только в run-time. В чем проблемы то?! Нам же нужен только агригат (контейней, если хотите), который делегирует вызовы конкретного интерфейса, конкретному экземпляру.

Проблема в том, что вместо использования уже готовых возможностей:
Variant<Widget, Gadget> x, y;
// ...
apply(x, y, [](auto &x, auto &y)
{
    draw(x);
    draw(y);
    collide(x, y);
});
вы собрались собирать какой-то тип, непонятно как, и непонятно зачем.
В коде выше apply выбирает выводит конкретные типы для x и y однократно, на основе их тэгов, и внутри лямбды нет никаких проверок тэгов. В том варианте который предлагаете вы, как я понял — на каждом вызове типа draw будет что-то типа virtual call.

G>В Nemerle — такой же фокус можно провернуть в compile-time. А сложность тут вообще не причем... этот код пишется ровно один раз.


Ок, допустим один раз. Что будет сгенерировано в качестве агрегата для Variant<Widget, Gadget> из примера выше? Какой интерфейс у этого агрегата?

EP>>Тем что такой синтаксис можно получить на базе boost::variant

G>Синтаксис-то тут причем?! Скажите лучше во что это вычислится? В variant?

Да, в чём проблема-то? В общую базу это нормально, а в variant — нет? Почему?
В следующем примере MATCH вычисляется в variant:
LIVE DEMO
#include <iterator>
#include <vector>
#include "match.hpp"

struct widget {};
struct gadget {};

void draw(widget) { println("drawing widget"); }
void draw(gadget) { println("drawing gadget"); }

int main()
{
    using namespace std;
    bool flags[] = {true, false, true, true, false};
    vector<variant<widget, gadget>> xs;
    
    for(auto f : flags)
        if(f)
            xs.push_back(widget{});
        else
            xs.push_back(gadget{});

    for(auto x : xs)
        MATCH(x)
            CASE(x IS _) { draw(x); }
        ;
    println();

    vector<variant<int, double>> ys;
    for(auto x : xs)
        ys.push_back
        (
            MATCH(x)
                CASE(x IS widget) { println("x IS widget"); return 1; }
                CASE(x IS gadget) { println("x IS gadget"); return 0.1; }
        );
    println();

    for(auto y : ys)
        MATCH(y)
            CASE(y IS int)    { println("y IS int"); }
            CASE(y IS double) { println("y IS double"); }
        ;
}
Вывод:
drawing widget
drawing gadget
drawing widget
drawing widget
drawing gadget

x IS widget
x IS gadget
x IS widget
x IS widget
x IS gadget

y IS int
y IS double
y IS int
y IS int
y IS double
Re[6]: Что должен возвращать if?
От: Sinclair Россия https://github.com/evilguest/
Дата: 24.10.14 09:48
Оценка:
Здравствуйте, gbear, Вы писали:
G>Смотрите, я выше высказался за разрешение неоднозначности при вычислении if-без-else. Действительно её можно снять вычисляя его в maybe. Проблема только в том, что это никакого отношения к вычислению if-с-else не имеет
G>Добиться "одинаковости" можно лишь вычислением и if-с-else тоже в maybe. Что весьма странно, согласитесь. Ведь никаких неоднозначностей при его вычмслении у нас нет. И никакие variant[A, B] и т.п. не дадут вам взаимозаменяемости, если хотите, между результатами вычислений if-с-else и if-без-else.

А зачем вы обязательно хотите одинаковости в вычислениях if-с-else и if-без-else?
На мой взгляд, это два разных оператора, которые вполне имеют право возвращать разные результаты.
Можно вообще считать else шорткатом для оператора ??.
То есть результат выражения if (x == 5) 42 имеет тип Nullable<int>.
Результат выражения if (x == 5) 42 else 41 имеет тот же тип, что и someNullableInt ?? 41, т.е. просто int.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[10]: Что должен возвращать if?
От: Sinclair Россия https://github.com/evilguest/
Дата: 24.10.14 09:57
Оценка:
Здравствуйте, gbear, Вы писали:

G>Почему только при присвоении?! Везде, где "по смыслу" должно быть T.

Везде, где по смыслу должно быть T, должен быть лифтинг.
Монада — она на то и монада.
Вот был у нас код (простите за шарп-синтаксис — других не умею) типа
int Test()
{
  var a = 6; // int
  var b = 7; // int
  var c = a * b; // int(42);
  return c;
}

Мы его улучшили:
int Test(bool b)
{
  var a = if(b) 6; // int?
  var b = 7; // int
  var c = a * b; // int? - лифтинг!
  return c; // compile-time error
}

Теперь у программиста есть на выбор несколько способов починить функцию:
а)
int Test(bool b)
{
  var a = if(b) 6 else 0; // int
  var b = 7; // int
  var c = a * b; // int 
  return c; // tadamm!
}


b)
int? Test(bool b)
{
  var a = if(b) 6; // int?
  var b = 7; // int
  var c = a * b; // int? - лифтинг!
  return c; // tadamm!
}

c)
int Test(bool b)
{
  var a = if(b) 6; // int?
  var b = 7; // int
  var c = a * b; // int? - лифтинг!
  return c ?? 0; // disambiguation
}
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[11]: Что должен возвращать if?
От: Evgeny.Panasyuk Россия  
Дата: 25.10.14 13:19
Оценка:
Здравствуйте, Sinclair, Вы писали:

G>>Почему только при присвоении?! Везде, где "по смыслу" должно быть T.

S>Везде, где по смыслу должно быть T, должен быть лифтинг.
S>Монада — она на то и монада.

Если у нас массив int? и мы вычисляем сумму всех элементов "обычным for", от первого до последнего элемента, то сколько будет всего проверок на null если у нас уже первый элемент окажется null?
Если N проверок — то здесь нет ничего монадического, это только аппликативный функтор. При полноценном использовании монады — была бы только одна проверка.

S>Вот был у нас код (простите за шарп-синтаксис — других не умею) типа

S> ...
S>
S>int Test(bool b)
S>{
S>  var a = if(b) 6; // int?
S>  var b = 7; // int
S>  var c = a * b; // int? - лифтинг!
S>  return c ?? 0; // disambiguation
S>}
S>


Подобный синтаксис возможен и для variant
Автор: Evgeny.Panasyuk
Дата: 17.10.14
.
Re[7]: Что должен возвращать if?
От: Evgeny.Panasyuk Россия  
Дата: 25.10.14 13:38
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>А зачем вы обязательно хотите одинаковости в вычислениях if-с-else и if-без-else?

S>На мой взгляд, это два разных оператора, которые вполне имеют право возвращать разные результаты.

Если и там и там использовать variant — то получается достаточно симметрично, и каких-то проблем я не вижу (при необходимости, текущее поведение получается явным вызовом одного унарного оператора). Причём if-без-else может иметь отдельный синтаксис а-ля when, для избежания dangling else.

S>Можно вообще считать else шорткатом для оператора ??.

S>То есть результат выражения if (x == 5) 42 имеет тип Nullable<int>.
S>Результат выражения if (x == 5) 42 else 41 имеет тот же тип, что и someNullableInt ?? 41, т.е. просто int.

А что для if(cond) 11 else 0.11?
Re[8]: Что должен возвращать if?
От: Sinclair Россия https://github.com/evilguest/
Дата: 27.10.14 06:16
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>Если и там и там использовать variant — то получается достаточно симметрично, и каких-то проблем я не вижу (при необходимости, текущее поведение получается явным вызовом одного унарного оператора).

Основная проблема — это накопление вложенных variant<variant<x, y>, x, y>.
Чтобы этого не происходило, нужно уметь сворачивать варианты, чтобы попытка получить variant<variant<x, y>, z> автоматически приводилась к variant<x, y, z>.
Тогда, как частный случай, мы бы получили в результатеif(BooleanFunc()) NullableIntFunc(); не variant<variant<int, void>, void>, а просто variant<int, void>, он же option<int>.

EP>Причём if-без-else может иметь отдельный синтаксис а-ля when, для избежания dangling else.

Синтаксис — штука интересная. Имея, скажем, оператор ??, можно вполне обойтись и без else, и без тернарного оператора. Если не настаивать на симметрии:
typeof(BooleanFunc() ? 5) => int?
typeof(BooleanFunc() ? 5 ?? 0) => int


EP>А что для if(cond) 11 else 0.11?

Либо выводить общий тип (и ругаться на несовместимость приведений), либо variant<int, float>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[15]: Что должен возвращать if?
От: gbear Россия  
Дата: 27.10.14 10:52
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

G>>Да я не про это. Кто будет "дергать за веревочку"?

EP>Программист вестимо, а кто ещё если это "явное" упрощение?
G>>И главное — как он будет определять нужно ли "дергать"?
EP>Самому или с помощью компилятора. Например сейчас же компилятор ругается на вывод Object'а?

Вот, при вычислении мы получили некий variant[int, void]. Вы, только честно, способны сказать _что_ мы получили? Ваше типо-maybe? Или, может быть, это "нормальный" variant?
Вы предлагаете интерпритировать _любой_ variant[T, void] как maybe чтоли?! А если T — внезапно — окажется void? if(x > 0) foo(x) — это тогда "go wrong" — без вариантов

А что делать в "обобщенном коде", когда любой variant[T, U], при таком подходе, способен внезапно стать таким типо-maybe?

Еще раз... maybe — вещь вполне конкретная. Обладает вполне определенной семантикой. variant — такой семантической нагрузки не несет. И "усилием воли" её на него "повесить" не выйдет.

G>>Есть foo(x) -> void и bar(x) -> variant[int, void].

G>>
G>>if(x > 0) bar(x) else foo(x)
G>>

G>>Нужно "дергать"?

EP>Если неявного упрощения нет — то да, можно.



Кхм... я дико извиняюсь. Но я таки спрашивал вас не за "можно", а за "нужно".

По остальному отпишусь отдельным постом

---
С уважением, Константин Сиваков
Re[9]: Что должен возвращать if?
От: Evgeny.Panasyuk Россия  
Дата: 27.10.14 10:53
Оценка:
Здравствуйте, Sinclair, Вы писали:

EP>>Если и там и там использовать variant — то получается достаточно симметрично, и каких-то проблем я не вижу (при необходимости, текущее поведение получается явным вызовом одного унарного оператора).

S>Основная проблема — это накопление вложенных variant<variant<x, y>, x, y>.
S>Чтобы этого не происходило, нужно уметь сворачивать варианты, чтобы попытка получить variant<variant<x, y>, z> автоматически приводилась к variant<x, y, z>.
S>Тогда, как частный случай, мы бы получили в результатеif(BooleanFunc()) NullableIntFunc(); не variant<variant<int, void>, void>, а просто variant<int, void>, он же option<int>.

Опять же, это можно делать одним явным вызовов унарного оператора, который по-максимуму упростит тип.
Основной вопрос в том, сколько неявности можно себе позволить, не сломав при этом обобщённый код (в который могут прийти и типы приводящие к variant'у который можно упростить и те для которых его упростить нельзя).

S>>>Результат выражения if (x == 5) 42 else 41 имеет тот же тип, что и someNullableInt ?? 41, т.е. просто int.

EP>>А что для if(cond) 11 else 0.11?
S>Либо выводить общий тип (и ругаться на несовместимость приведений), либо variant<int, float>

Опять таки, возникает вопрос совместимости с обобщённым кодом: if(x == 5) T() else U() — тут T и U могут быть одинаковые или разные.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.