Информация об изменениях

Сообщение Re[39]: EntityFramework - тормоз от 24.04.2015 15:27

Изменено 24.04.2015 15:31 Evgeny.Panasyuk

Здравствуйте, Sinclair, Вы писали:

S>>>Ок, я понял. Обстоят чуть хуже, чем никак:

S>>>
S>>>    auto x = select(apples.appleId, oranges.orangeId); // WTF is x???
S>>>

EP>>Это же простой пример не претендующий на полноту. При желании можно получить например такой синтаксис:
S>1. простой вопрос: так что же всё-таки есть в этом sqlpp, кроме добрых намерений? А то вот некоторые по соседству смело утверждают, что есть прямо таки всё и из коробки. А вы пишете про "при желании"...

Я показываю не sqlpp11, а технику которая может использоваться для создания структуры с необходимым набором полей (а не просто безликий котреж), причём наполняя её в зависимости от каких-либо compile-time условий.

S>Я, не имея С++ компилятора под рукой, склоняюсь к тому, что sqlpp11 умеет проверять только простейшие сценарии.


sqlpp11 я знаю только по нескольким примерам, он умеет ли он или нет "сложные" сценарии — ручаться не могу. Тем не менее если интересны какие-то конкретные моменты sqlpp11 — можем здесь разобрать.

S>2. более сложный вопрос: а за счёт чего и как мы планируем порождать типы и контролировать корректность статически?


За счёт того что у нас во время компиляции известны: структура таблиц, связи между ними, дерево выражения запроса — и для обработки этого есть язык времени компиляции полный по Тьюрингу.

S>Попробуйте написать простейший запрос типа "покажи мне всех менеджеров, которые продали больше среднего в своих отделах в прошлом году". На sql это пишется тривиально:


На чём? На sqlpp11 или показать абстрактный вариант compile-time EDSL?

S>Linq позволит мне разнести этапы определения CTE и сборки запроса, отчего код сильно выиграет в читаемости и редактируемости.


Разнести этапы составления дерева выражения времени компиляции по разным местам вообще не проблема.
Для этого даже не нужно ничего делать специально — достаточно только не мешать.

Пример:
// Terminal symbols:
struct Select {} select;
struct Where {} where;

// Nonterminal:
template<typename Left, typename Right> struct Pipeline {};

template<typename T, typename U>
auto operator|(T, U)
{
    return Pipeline<T, U>{};
}

// Utility for print type at compile time:
template<typename T> void type_is(T);

int main()
{
    // Create expression tree by parts, in different expressions:
    // 1:
    auto q1 = select | where;
    // 2:
    auto q2 = q1 | select;
    // Print type of q2:
    type_is(q2);
}
Результат:
undefined reference to `void type_is<Pipeline<Pipeline<Select, Where>, Select> >(Pipeline<Pipeline<Select, Where>, Select>)'

Жирным выделен тип. Этот тип можно обрабатывать во время компиляции.
Например можем запретить построение выражения вида * | where | select | where | *:
// Process expression tree:
template<typename _>
struct CheckGrammar
{
    using type = bool;
};

template<typename Left, typename Right>
struct CheckGrammar<Pipeline<Left, Right>>
{
    using type = typename CheckGrammar<Left>::type; // Recurse to left sub-tree
};

template<typename _>
struct CheckGrammar
<
    Pipeline<Pipeline<Pipeline<_, Where>, Select>, Where> // Pattern matching
>
{
    using type = typename _::error_in_grammar_after;
};

template<typename T, typename U>
auto operator|(T, U)
{
    auto result = Pipeline<T, U>{};
    using run = typename CheckGrammar<decltype(result)>::type; // Run grammar check at compile-time
    return result;
}

int main()
{
    auto q1 = where | select | select; // OK
    auto q2 = q1 | where; // OK
    auto q3 = q2 | select | where; // ERROR
}
Вывод компилятора:
  Скрытый текст
main.cpp:31:30: error: no type named 'error_in_grammar_after' in 'Pipeline<Pipeline<Where, Select>, Select>'
    using type = typename _::error_in_grammar_after;
                 ~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~

main.cpp:38:26: note: in instantiation of template class 'CheckGrammar<Pipeline<Pipeline<Pipeline<Pipeline<Pipeline<Where, Select>, Select>, Where>, Select>, Where> >' requested here
    using run = typename CheckGrammar<decltype(result)>::type; // Run grammar check at compile-time
                         ^

main.cpp:46:27: note: in instantiation of function template specialization 'operator|<Pipeline<Pipeline<Pipeline<Pipeline<Where, Select>, Select>, Where>, Select>, Where>' requested here
    auto q3 = q2 | select | where; // ERROR

Подобное дерево можно обрабатывать как угодно. Результатом compile-time обработки, опять таки полной по Тьюрингу, может быть строка запроса.
Можно расширить пример полями из предыдущего примера, и наполнять ими struct, делать mapping, в зависимости от структуры дерева выражения.

S>При этом шаг вправо-влево будет отслеживаться компилятором, т.к. в процессе сборки дерева выводятся типы промежуточных результатов и всё проверяется.


Это тоже не проблема.

S>На первый взгляд, sqlpp не предлагает ничего интереснее констант для имён таблиц и полей.


В документации написано что есть под-запросы. При этом повторюсь:

http://rsdn.ru/forum/flame.comp/6014725.1


EP>Изначально речь шла про принципиальную возможность. А возможно это не только в C++, но и например в D или Nemerle.
EP>Что же касается конкретного примера — я с СУБД вообще не работаю. Для создания полного примера мне придётся разбираться с одной из упомянутых выше библиотек — на первый взгляд то о чём я говорю там вполне реализуемо, но могут быть некоторые детали которые будут препятствовать реализации, что впрочем никак не будет означать принципиальную невозможность, и соответственно к изначальному тезису не имеет отношения.

EP>Если же есть сомнения в принципиальной реализуемости — то готов ответить на конкретные вопросы.

Здравствуйте, Sinclair, Вы писали:

S>>>Ок, я понял. Обстоят чуть хуже, чем никак:

S>>>
S>>>    auto x = select(apples.appleId, oranges.orangeId); // WTF is x???
S>>>

EP>>Это же простой пример не претендующий на полноту. При желании можно получить например такой синтаксис:
S>1. простой вопрос: так что же всё-таки есть в этом sqlpp, кроме добрых намерений? А то вот некоторые по соседству смело утверждают, что есть прямо таки всё и из коробки. А вы пишете про "при желании"...

Я показываю не sqlpp11, а технику которая может использоваться для создания структуры с необходимым набором полей (а не просто безликий котреж), причём наполняя её в зависимости от каких-либо compile-time условий.

S>Я, не имея С++ компилятора под рукой, склоняюсь к тому, что sqlpp11 умеет проверять только простейшие сценарии.


sqlpp11 я знаю только по нескольким примерам, умеет ли он или нет "сложные" сценарии — ручаться не могу. Тем не менее, если интересны какие-то конкретные моменты sqlpp11 — можем здесь разобрать.

S>2. более сложный вопрос: а за счёт чего и как мы планируем порождать типы и контролировать корректность статически?


За счёт того что у нас во время компиляции известны: структура таблиц, связи между ними, дерево выражения запроса — и для обработки этого есть язык времени компиляции полный по Тьюрингу.

S>Попробуйте написать простейший запрос типа "покажи мне всех менеджеров, которые продали больше среднего в своих отделах в прошлом году". На sql это пишется тривиально:


На чём? На sqlpp11 или показать абстрактный вариант compile-time EDSL?

S>Linq позволит мне разнести этапы определения CTE и сборки запроса, отчего код сильно выиграет в читаемости и редактируемости.


Разнести этапы составления дерева выражения времени компиляции по разным местам вообще не проблема.
Для этого даже не нужно ничего делать специально — достаточно только не мешать.

Пример:
// Terminal symbols:
struct Select {} select;
struct Where {} where;

// Nonterminal:
template<typename Left, typename Right> struct Pipeline {};

template<typename T, typename U>
auto operator|(T, U)
{
    return Pipeline<T, U>{};
}

// Utility for print type at compile time:
template<typename T> void type_is(T);

int main()
{
    // Create expression tree by parts, in different expressions:
    // 1:
    auto q1 = select | where;
    // 2:
    auto q2 = q1 | select;
    // Print type of q2:
    type_is(q2);
}
Результат:
undefined reference to `void type_is<Pipeline<Pipeline<Select, Where>, Select> >(Pipeline<Pipeline<Select, Where>, Select>)'

Жирным выделен тип. Этот тип можно обрабатывать во время компиляции.
Например можем запретить построение выражения вида * | where | select | where | *:
// Process expression tree:
template<typename _>
struct CheckGrammar
{
    using type = bool;
};

template<typename Left, typename Right>
struct CheckGrammar<Pipeline<Left, Right>>
{
    using type = typename CheckGrammar<Left>::type; // Recurse to left sub-tree
};

template<typename _>
struct CheckGrammar
<
    Pipeline<Pipeline<Pipeline<_, Where>, Select>, Where> // Pattern matching
>
{
    using type = typename _::error_in_grammar_after;
};

template<typename T, typename U>
auto operator|(T, U)
{
    auto result = Pipeline<T, U>{};
    using run = typename CheckGrammar<decltype(result)>::type; // Run grammar check at compile-time
    return result;
}

int main()
{
    auto q1 = where | select | select; // OK
    auto q2 = q1 | where; // OK
    auto q3 = q2 | select | where; // ERROR
}
Вывод компилятора:
  Скрытый текст
main.cpp:31:30: error: no type named 'error_in_grammar_after' in 'Pipeline<Pipeline<Where, Select>, Select>'
    using type = typename _::error_in_grammar_after;
                 ~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~

main.cpp:38:26: note: in instantiation of template class 'CheckGrammar<Pipeline<Pipeline<Pipeline<Pipeline<Pipeline<Where, Select>, Select>, Where>, Select>, Where> >' requested here
    using run = typename CheckGrammar<decltype(result)>::type; // Run grammar check at compile-time
                         ^

main.cpp:46:27: note: in instantiation of function template specialization 'operator|<Pipeline<Pipeline<Pipeline<Pipeline<Where, Select>, Select>, Where>, Select>, Where>' requested here
    auto q3 = q2 | select | where; // ERROR

Подобное дерево можно обрабатывать как угодно. Результатом compile-time обработки, опять таки полной по Тьюрингу, может быть строка запроса.
Можно расширить пример полями из предыдущего примера, и наполнять ими struct в зависимости от структуры дерева выражения, отображать них результат запроса и т.д. и т.п.

S>При этом шаг вправо-влево будет отслеживаться компилятором, т.к. в процессе сборки дерева выводятся типы промежуточных результатов и всё проверяется.


Это тоже не проблема.

S>На первый взгляд, sqlpp не предлагает ничего интереснее констант для имён таблиц и полей.


В документации написано что есть под-запросы.
При этом повторюсь:

http://rsdn.ru/forum/flame.comp/6014725.1


EP>Изначально речь шла про принципиальную возможность. А возможно это не только в C++, но и например в D или Nemerle.
EP>Что же касается конкретного примера — я с СУБД вообще не работаю. Для создания полного примера мне придётся разбираться с одной из упомянутых выше библиотек — на первый взгляд то о чём я говорю там вполне реализуемо, но могут быть некоторые детали которые будут препятствовать реализации, что впрочем никак не будет означать принципиальную невозможность, и соответственно к изначальному тезису не имеет отношения.

EP>Если же есть сомнения в принципиальной реализуемости — то готов ответить на конкретные вопросы.