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
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.