LINUX.ORG.RU

История изменений

Исправление quasimoto, (текущая версия) :

А если так

Так ты написал функцию id : fin<3> -> fin<3> в compile time, а нам надо сам этот тип для рантайма — населить fin<n> числами от 0 до n - 1:

// fin<3> -- тип из, ровно, 0, 1 и 2.
fin<3> x = 0;
fin<3> x = 1;
fin<3> x = 2;
// fin<3> x = 3; <- error

с кучей enum-ов под каждый n получается

fin<3> x = fin<3>::_0;
fin<3> x = fin<3>::_1;
fin<3> x = fin<3>::_2;
// fin<3> x = fin<3>::_3; <- error

причём n из fin<n> — в compile time, а вот значения типа fin<n> — в runtime, все меньше n, так что можно написать функции optional<fin<n>> to_fin(size_t) и size_t from_fin(fin<n>) и использовать такие ограниченные индексы в рантайме для безопасной индексации к std::array как T get(std::array<T, n> const& xs, fin<n> i) (видим, что i < n на уровне сигнатуры). optional нужен, choose — тоже, это типа «разыменование указателей done right» (eliminator для option/maybe, вообще, тип безопасно можно использовать только с помощью introduction (конструкторы), elimination (визиторы общего вида) и производных тотальных функций, всё остальное частичное и в обход introduction и elimination вроде разыменования указателя или get для optional в основе не безопасно и должно быть просто приватным / проверенным / на свой страх и риск, если интерфейс делается заведомо сильно типобезопасным), то есть это решение вопроса «как система типов будет угадывать, что сырые числа будут лежать в нужных границах во время выполнения?» — ничем не отличается от вопроса «как parse_integer будет угадывать, что строка будет валидным числом?», с помощью optional — если нет частичных функций / обхода правил типизации, то тут проблем не может быть в принципе.

Ещё такой fin можно сделать как рекурсивный

template<size_t n> struct fin_r { virtual size_t value() const = 0; };
template<size_t n> struct z : fin_r<n> { size_t value() const { return 0; } };
template<size_t n> struct s : fin_r<n> {
    fin_r<n - 1> const& prev;
    s(fin_r<n - 1> const& prev_) : prev(prev_) {}
    size_t value() const { return 1 + prev.value(); }
};

но

fin_r<3> const& x = z<3>{};
fin_r<3> const& x = s<3>(z<2>{});
fin_r<3> const& x = s<3>(s<2>(z<1>{}));

явно не удобно, плюс оно тормозит.

Исходная версия quasimoto, :

А если так

Так ты написал функцию id : fin<3> -> fin<3> в compile time, а нам надо сам этот тип для рантайма — населить fin<n> числами от 0 до n - 1:

// fin<3> -- тип из, ровно, 0, 1 и 2.
fin<3> x = 0;
fin<3> x = 1;
fin<3> x = 2;
// fin<3> x = 3; <- error

с кучей enum-ов под каждый n получается

fin<3> x = fin<3>::_0;
fin<3> x = fin<3>::_1;
fin<3> x = fin<3>::_2;
// fin<3> x = fin<3>::_3; <- error

причём n из fin<n> — в compile time, а вот значения типа fin<n> — в runtime, все меньше n, так что можно написать функции optional<fin<n>> to_fin(size_t) и size_t from_fin(fin<n>) и использовать такие ограниченные индексы в рантайме для безопасной индексации к std::array как T get(std::array<T, n> const& xs, fin<n> i) (видим, что i < n на уровне сигнатуры). optional нужен, choose — тоже, это типа «разыменование указателей done right» (eliminator для option/maybe, вообще, тип безопасно можно использовать только с помощью introduction (конструкторы), elimination (визиторы общего вида) и производных тотальных функций, всё остальное частичное и в обход introduction и elimination вроде разыменования указателя или get для optional в основе не безопасно и должно быть просто приватным / проверенным / на свой страх и риск, если интерфейс делается заведомо сильно типобезопасным), то есть это решение вопроса «как система типов будет угадывать, что сырые числа будут лежать в нужных границах во время выполнения?» — ничем не отличается от вопроса «как parse_integer будет угадывать, что строка будет валидным числом?», с помощью optional — если нет частичных функций / обхода правил типизации, то тут проблем не может быть в принципе.

Ещё такой fin можно сделать как рекурсивный

template<size_t n> struct fin_r { virtual size_t value() const = 0; };
template<size_t n> struct z : fin_r<n> { size_t value() const { return 0; } };
template<size_t n> struct s : fin_r<n> {
    fin_r<n - 1> const& prev;
    s(fin_r<n - 1> const& prev_) : prev(prev_) {}
    size_t value() const { return 1 + prev.value(); }
};

но

fin_r<3> const& x = s<3>(s<2>(z<1>{}));

явно не удобно, плюс оно тормозит.