Данный документ призван специфицировать единую систему типов в Yandex и правила ее развития.

В задачи спецификации входит:
- описать полный список типов
- для каждого типа зафиксировать: именование, способ сериализации, поведение типа, ограничения на тип.
- регламентировать способ добавления новых типов в систему

На данный момент спецификация:
- никак не регламентирует хранение типов в сторадже и памяти, каждая система может решить это по своему, оптимально для своего случая
- никак не описывает не персистентные типы (которые, например, могут появиться в результате промежуточных вычислений), так как применения их в рамках 2х систем пока не выявлено.
Все это, тем не менее, не отрицает развития в будущем по этим вопросам.

В задачи спецификации входит:
- описать полный список типов
- для каждого типа зафиксировать - именование, способ сериализации, "базовую" семантику.
- регламентировать способ добавления новых типов в систему.

В конечном итоге, все системы хранения и обработки структурированных данных, создаваемые в компании,
должны будут поддерживать данную систему типов. 

Все типы из спецификации, их сериализация и часть свойств отражены в коде библиотеки [library/cpp/type\_info](https://a.yandex-team.ru/arc/trunk/arcadia/library/cpp/type_info).

### Типы

Для обобщения специфики типов введена классификация. Каждый тип относится к одному конкретному классу.

1. **Примитивный (простой/элементарный) тип данных** - тип, который не содержит в параметрах другие типы, т.е. не имеет внутренних компонентов. 
В памяти может быть представлен непрерывным буфером и всегда сериализуем. Примитивные типы бывают, например: числовые, логические, строковые и временнЫе

2. **Сингулярный тип данных** - тип с единственным значением. Всегда сериализуем. Из-за своего определения может не требовать буфера для передачи данных этого типа.

3. **Составной тип данных** - тип, у которого есть хотя бы один параметр - вложенный тип. Данные составного типа всегда сериализуемы, если все его вложенные элементы сериализуемы.

4. **Специальный тип данных** - тип, который не попадает под вышеперечисленные категории.



### Примитивные типы
**Логические:**
- Bool - логический тип, имеющий два значения: true и false.

**Числовые:**
- Int8 - Целое знаковое число, размерности 1 байт, диапазон значений ``[-2^7, 2^7-1]``.
- Int16 - Целое знаковое число, размерности 2 байта, диапазон значений `[-2^15, 2^15-1]`.
- Int32 - Целое знаковое число, размерности 4 байта, диапазон значений `[-2^31, 2^31-1]`.
- Int64 - Целое знаковое число, размерности 8 байт, диапазон значений `[-2^63, 2^63-1]`.
- Uint8 - Целое беззнаковое число, размерности 1 байт, диапазон значений `[0, 2^8-1]`.
- Uint16- Целое беззнаковое число, размерности 2 байт, диапазон значений `[0, 2^16-1]`.
- Uint32- Целое беззнаковое число, размерности 4 байт, диапазон значений `[0, 2^32-1]`.
- Uint64 - Целое беззнаковое число, размерности 8 байт, диапазон значений `[0, 2^64-1]`.
- Float, Double - Вещественные числа с переменной точностью, 4 и 8 байт соответственно.
- Decimal - Вещественные числа с указанной точностью, до 35 десятичных знаков. `Decimal<Precision, Scale>`, Precision контролирует количество десятичных знаков, от 0 до 35 включительно. Scale контролирует, сколько из этих знаков находится после запятой, от 0 до Precision включительно. У него есть специальные состояния NaN, sNaN, +Inf, -Inf, +0, -0.

**Строковые:**
- String - Бинарная строка произвольной длины.
- Utf8 - Корректная utf-8 последовательность.
- Json - Cтрока с валидным json представлением в соответствии с [RFC-7159](https://tools.ietf.org/html/rfc7159).
- Yson - Cтрока с валидным yson представлением (текстовым или бинарным), физически представляется последовательностью байт.
- Uuid - universally unique identifier, в соответствии с RFC-4122.

**Типы для работы со временем:**
Все значения времени ограничены интервалом от 1970 года включительно до 2106 года не включительно. 

- Date - количество дней, прошедших с unix-эпохи (1970 год). Кодируется, как Uint16 в диапазоне ``[0, 49673 - 1]`` .  Допустимый диапазон времен ``[1970-01-01, 2105-12-31]``.
- Datetime — количество секунд, прошедших с unix-эпохи (1970 год). Кодируется, как Uint32 в диапазоне ``[0, 49673 * 86400 - 1]``.  Допустимый диапазон времен ``[1970-01-01T00:00:00Z, 2105-12-31T23:59:59Z]`` .
- Timestamp - количество микросекунд, прошедших с unix-эпохи (1970 год). Кодируется, как Uint64 в диапазоне ``[0, 49673 * 86400 * 10^6 - 1]``. Допустимый диапазон времен ``[1970-01-01T00:00:00Z, 2105-12-31T23:59:59.999999Z]``.
- Interval - количество милисекунд между двумя точками во времени, кодируется как Int64. Допустимый диапазон ``[- 49673 * 86400 * 10^6 + 1, 49673 * 86400 * 10^6 - 1]``.


### Сингулярные типы:
- Null - сингулярный тип данных с единственным возможным значением null. Является типом литерала NULL и может преобразовываться к любому Optional типу.
- Void - сингулярный тип данных с единственным возможным значением null.
- EmptyList — сингулярный тип данных с единственным возможным значением []. Может преобразовываться к любому List типу. Поддерживает все те же операции, как и обычные List, при этом в операции Lookup возращается Null тип, т.е. тип значения неизвестен. ListLength возвращают 0-и. Можно выполнять ListMap/ListFilter - результат безусловный EmptyList.
- EmptyDict — сингулярный тип данных с единственным возможным значением {}.  может преобразовываться к любому Dict типу. Поддерживает все те же операции, как и обычные Dict, при этом в операции Lookup возращается Null тип, т.е. тип значения неизвестен. DictLength возвращают 0-и. 

### Составные типы
Типы, предоставляющие распространенные структуры данных.

- List/Список ```List<Type>``` - Последовательность переменной длины, состоящая из элементов одного типа Type.
- Struct/Структура ``Struct<Name1:Type1,...,NameN:TypeN>`` - Набор именованных элементов с указанными типами значений.
- Tuple/Кортеж ``Tuple<Type1,...,TypeN>`` - Набор безымянных элементов фиксированной длины с указанными типами всех элементов. 
- Variant/Вариант ``Variant<Type1,Type2>`` ``Variant<Name1:Type1,Name2:Type2>`` - Тип, про который известно, что заполнен ровно один элемент. 
Вариант бывает над струкрурой и кортежем. Вариант над структурой, тогда каждый тип имеет имя (как в структуре) и каждое значение варианта маркировано именем соответствующего элемента варианта;
Вариант над кортежем, тогда все элементы безымянные и каждое значение маркируется индексом.
- Dict/Словарь ``Dict<KeyType,ValueType>`` - Последовательность пар ключ—значение с фиксированным типом ключей и значений.
- Tagged/Тегированное значение  - возможность дать прикладное имя какому-либо другому типу, то есть аннотировать другие типы строкой
- Optional/Опциональный тип ``Optional<Type>`` -  контейнер, который содержит либо значение типа Type, либо отсутсвующее значение.

### Сериализация типов
Подробно описана [здесь](types_serialization.md)

### Именования типов

Имена типов могут быть указаны только опрделенным образом и в двух нотациях PascalCase/snake_case.

Имена типов должны быть валидными utf8.

Прямо сейчас НЕ везде разрешены оба написания. Это будет исправлено со временем.

- В yson схеме YT разрешен только snake_case.
- В YT в системном атрибуте @schema разрешен только snake_case.
- В YQL yql_row_spec - только	PascalCase.
- В YT в UI только PascalCase.
- В YQL в UI только PascalCase.
- В YQL QUERY в тех частях, которые из SQL грамматики, поддерживаются оба написания.
- В YQL QUERY вне SQL грамматики только PascalCaseю
- В YQL UDF Type Annotation только PascalCase.


|PascalCase|snake_case|
| --- | --- | 
|Null	|null|
|Void	|void|
|Double	|double|
|Float	|float|
|Decimal|	decimal|
|Int8, Int16, Uint8, Uint16	|int8, int16, uint8, uint16|
|Int32, Int64, Uint8, Uint16	|int32, int64, unit32, uint64|
|Bool|	bool|
|Utf8	|utf-8|
|String|	string|
|Json	|json|
|Yson	|yson|
|Uuid	|uuid|
|Date	|date|
|Datetime|	datetime|
|Timestamp|	timestamp|
|Interval|	interval|
|TzDate TzDatetime TzTimestamp	|tz_date, tz_datetime, tz_timestamp|
|Struct	|struct|
|Tuple|	tuple|
|Variant|	variant|
|List	|list|
|Dict	|dict|
|Set	|set|
|Enum	|enum|
|Tagged	|tagged|
|Optional	|optional|




### Свойства типов

Разное поведение типов в разных системах может приводить к неожиданным спецэффектам, поэтому необходимо его специфицировать.

[Раздел в стадии согласования]


### Ограничения на типы

Минимальные границы, которые обязаны соблюдать все системы специфицированы для каждого ограничения. При этом каждая система внутри себя может разрешать эти границы превышать.

Функции вычисления ограничений [будут](https://st.yandex-team.ru/YT-12042) доступны из библиотеки [library/cpp/type\_info](https://a.yandex-team.ru/arc/trunk/arcadia/library/type_info/type_info).

**Максимальное количество элементов**

Определено для структур, кортежей, варианта.

Максимальное количество элементов, которое поддерживают все системы составляет 65535.

**Сложность типа** 

Ограничение на сложность типа позволяет более точно учитывать случаи составных типов.
Сложность типа `T` (`Complexity(T)`) определяется рекурсивно.
Сложность схемы таблицы это сумма сложностей всех типов колонок.
Максимальная сложность схемы, которую точно поддерживают все системы ограничена сверху `32768 == 32 * 1024`.

| **Тип** | **Сложность**  |
| ------------ | ------------------- |
| примитивный или сингулярный тип: `int64`, `yson/any`, `utf8`, `null`... |```Complexity(T) := 1``` |
|`T == optional<P>`, где `P` любой тип | `Complexity(T) := 1 + Complexity(P)`|
|`T == list<P>` или `T == tagged<"some-tag", P>`, где `P` это любой другой тип |`Complexity(T) := 1 + Complexity(P)`|
|`T == struct<name_1:P_1, ..., name_N:P_N>`|`Complexity(T) := 1 + Complexity(P_1) + ... + Complexity(P_N)`|
|`T == tuple<P_1, ..., P_N>` |`Complexity(T) := 1 + Complexity(P_1) + ... + Complexity(P_N)`|
|`T == variant<P>`, где `P` - это структура или кортеж) |`Complexity(T) := Complexity(P)`.
|`T == dict<K,V>`|`Complexity(T) := 1 + Complexity(K) + Complexity(V)`|


**Длина названия элемента.**

Определена для структур, варианта над структурой.

Максимальная длина названия элемента, которую поддерживают все системы 256 символов.



### Регламент расширение системы типов
Все типы описанные в спецификации должны быть поддержаны всеми системами (актуальный список в работе YT/YQL/RTMR/YDB).

Все системы добавляют новые типы только через единый процесс.

В единой системе типов все типы для пользователя должны быть различны, даже если на уровне имплементации некоторые типы ведут себя одинаково, то пользователь не должен про это знать.

Для заведения нового типа требуется:
1. Подготовка proposal с обоснованием появления нового типа
- Мотивация для появления нового типа (кому/зачем/какую проблему решает)
- Аналоги в других системах/стандартах
- Сериализация типа в yson, protobuf, skiff
- Свойства типа 
- Конвертируемость типов в другие
2. Отправка proposal на data-com@.
3. Обсуждение в data-комитете. Решение выносится  после очного обсуждения, если единогласного решения нет, то путем голосования (список утвердим)
4. Если комитет одобряет заведение типа, то тип добавляется в спецификацию и в библиотеку типов type_info, а все системы получают задачу на его поддержку.
Комитет может отклонить заведение типа с мотивацией, это означает, что никакие системы не будут гарантировать адекватную работу с подобными данными.





