Skip to content

lawref.values

Типизированные значения и term-вычисление (WP-06; §18, §48–§50, §57–§58).

Точный Decimal (stdlib, float запрещён §18); Quantity с v1-алгеброй размерностей (§49: сложение/сравнение — только одинаковые единицы, умножение/деление на скаляр; канонические конверсии единиц — вместе с unit-package WP-31+); Money (§50: межвалютная арифметика запрещена — CURRENCY_MISMATCH, T022; округление никогда не подразумевается); Date/Instant не сравниваются между собой (§57, §2.15 — T024). Отсутствие implicit conversions — §48.

Значение результата рендерится каноническим LiteralTerm-объектом (kind value, Decimal — canonical non-exponent string §18/§208).

Attributes

NameDescription
AGGREGATE_OPSNo description.
BOOLEANNo description.
BOUNDSNo description.
CALENDAR_PERIOD_ONLY_UNITSNo description.
CALENDAR_PERIOD_UNITSNo description.
DATENo description.
DAY_UNITSNo description.
DEADLINE_UNITSNo description.
DECIMALNo description.
DIV_ROUND_MAX_PRECISIONNo description.
INSTANTNo description.
INTEGERNo description.
MAGNITUDENo description.
MONEYNo description.
NATURALNo description.
POW_EXP_LIMITNo description.
POW_VALUE_MAXNo description.
POW_VALUE_MINNo description.
QUANTITYNo description.
RATIONALNo description.
ROUNDING_MODESNo description.
SCALAR_TYPE_NAMESNo description.
TEXTNo description.

Classes

NameDescription
TypedТипизированное значение: kind ∈ {decimal, money, quantity, date, instant,
ValueError_Ошибка term-вычисления; code — машинный код для issue.

Functions

NameDescription
add_business_days_value§86 add_business_days(after, count, calendar, policy) термом.
add_calendar_period_value§84.2/§86 add_calendar_period(after, count, policy) — errata E-0029.
add_duration_value§84.1: физическая Duration после Instant; ответ каноничен в UTC.
add_legal_term_value§86.1: runtime-unit dispatch для duration-binder сахара term.
aggregateАгрегаты §59 над материализованной коллекцией. Пустая коллекция без
aggregate_element_type§52.1/§59 (errata E-0195): вид элемента коллекции по ОБЪЯВЛЕННОМУ типу
binaryBinaryTerm-операции §58 (+ - * / mod) и сравнения §57 (lt/le/gt/ge/eq/ne).
callCallTerm: std-функции §50/§58/§86 над вычисленными аргументами.
convert_value§49 (errata E-0072): перевод единицы ЯВНОЙ формой — точный фактор парой целых.
days_between_value§84.4 (errata E-0015): число календарных дней от start до end.
div_round_value§50/§58 (errata E-0036): частное, округлённое ЯВНОЙ политикой.
eval_data_conditionЗначение чистого data-условия §56 под связанными переменными.
eval_termВычисление чистого CLIR data-терма.
from_literal_termLiteralTerm (kind value) → Typed. Тип определяется TypeRef.name + unit/currency.
hours_between_value§84.1 (errata E-0028): число ПОЛНЫХ часов от start до end.
is_data_condition§56: условие IfTerm — ЧИСТОЕ Boolean-выражение данных.
minutes_between_value§84.1 (errata E-0058): число ПОЛНЫХ минут от start до end.
month_bound_value§84.2 (errata E-0084): первое (end=False) либо последнее число месяца
raw_jsonСырое поле литерала в тексте issue — КАНОНОМ §208, не repr хоста.
round_fractionПубличный псевдоним roundfraction для §64.2 (errata E-0136).
round_value§50 round(value, precision, mode): единственный законный способ получить
select_match_arm§55.1: первая совпавшая arm и её связывания, либо None.
text_length_value§257 (errata E-0166): число кодовых точек (Unicode scalar values) текста
to_literal_termTyped → literal-терм §53 (обратное к from_literal_term).
weekday_value§84.2 (errata E-0091): день недели даты day — Integer от 1
with_unit_value§49 (errata E-0074): пришивка «число → величина» ЯВНОЙ формой.

AGGREGATE_OPSattributemodule attribute#

AGGREGATE_OPS = ('sum', 'count', 'min', 'max', 'average')

BOOLEANattributemodule attribute#

BOOLEAN = 'urn:law:std#Boolean'

BOUNDSattributemodule attribute#

BOUNDS = 'urn:law:std#Bounds'

CALENDAR_PERIOD_ONLY_UNITSattributemodule attribute#

CALENDAR_PERIOD_ONLY_UNITS = ('calendar_month', 'calendar_year', 'calendar_week')

CALENDAR_PERIOD_UNITSattributemodule attribute#

CALENDAR_PERIOD_UNITS = ('calendar_day', 'calendar_month', 'calendar_year', 'calendar_week')

DATEattributemodule attribute#

DATE = 'urn:law:std#Date'

DAY_UNITSattributemodule attribute#

DAY_UNITS = ('business_day', 'calendar_day')

DEADLINE_UNITSattributemodule attribute#

DEADLINE_UNITS = ('business_day', 'calendar_day', 'calendar_month', 'calendar_year', 'calendar_week')

DECIMALattributemodule attribute#

DECIMAL = 'urn:law:std#Decimal'

DIV_ROUND_MAX_PRECISIONattributemodule attribute#

DIV_ROUND_MAX_PRECISION = 34

INSTANTattributemodule attribute#

INSTANT = 'urn:law:std#Instant'

INTEGERattributemodule attribute#

INTEGER = 'urn:law:std#Integer'

MAGNITUDEattributemodule attribute#

MAGNITUDE = 'urn:law:std#Magnitude'

MONEYattributemodule attribute#

MONEY = 'urn:law:std#Money'

NATURALattributemodule attribute#

NATURAL = 'urn:law:std#Natural'

POW_EXP_LIMITattributemodule attribute#

POW_EXP_LIMIT = 4096

POW_VALUE_MAXattributemodule attribute#

POW_VALUE_MAX = 2 ** 63 - 1

POW_VALUE_MINattributemodule attribute#

POW_VALUE_MIN = -2 ** 63

QUANTITYattributemodule attribute#

QUANTITY = 'urn:law:std#Quantity'

RATIONALattributemodule attribute#

RATIONAL = 'urn:law:std#Rational'

ROUNDING_MODESattributemodule attribute#

ROUNDING_MODES = {
  'HALF_UP': 'ROUND_HALF_UP',
  'HALF_EVEN': 'ROUND_HALF_EVEN',
  'HALF_DOWN': 'ROUND_HALF_DOWN',
  'UP': 'ROUND_UP',
  'DOWN': 'ROUND_DOWN',
  'CEILING': 'ROUND_CEILING',
  'FLOOR': 'ROUND_FLOOR'
}

SCALAR_TYPE_NAMESattributemodule attribute#

SCALAR_TYPE_NAMES = frozenset(_TYPE_KINDS)

TEXTattributemodule attribute#

TEXT = 'urn:law:std#Text'

Typedclassdataclass#

class Typed(kind: str, value: Any, tag: str | None = None)

Типизированное значение: kind ∈ {decimal, money, quantity, date, instant, boolean, text}; для money tag = валюта (§50), для quantity tag = unit-символ (§49, v1: символ представляет размерность до unit-package).

kind == “enum” (§38/§57, errata E-0067): value — StableId члена {namespace}#{Член} (§24), tag — StableId объявившего перечисления. Равенство §57 сравнивает qualified identity; порядок не определён.

kindattributeinstance attribute#

kind: str

tagattributeclass attributeinstance attribute#

tag: str | None = None

valueattributeinstance attribute#

value: Any

ValueError_class#

class ValueError_(code: str, message: str)

Bases: Exception

Ошибка term-вычисления; code — машинный код для issue.

codeattributeinstance attribute#

code = code

messageattributeinstance attribute#

message = message

add_business_days_valuefunction#

def add_business_days_value(after: Typed, count: Typed, after_time: Typed | None, env: Any, where: str = 'add_business_days') -> Typed

§86 add_business_days(after, count, calendar, policy) термом.

Календарь §85 и DeadlinePolicy §86 приходят окружением (env), а не аргументами: в нормативном примере §86 это context.calendar и context.deadline_policy. Позиционная форма с единицей на счётчике — та же конвенция, что у round §50: v1-парсер именованных аргументов не имеет, а count = 10 business_day §84.3 записывается Quantity-литералом, и единица счёта остаётся видимой в тексте нормы.

До этой функции срок можно было только СПРОСИТЬ (calendar_op-запрос над литеральной датой) — вывести его правилом было нечем, и процедурные сроки приходилось вводить фактами, то есть считать вне CLIR.

add_calendar_period_valuefunction#

def add_calendar_period_value(after: Typed, count: Typed, env: Any, where: str = 'add_calendar_period') -> Typed

§84.2/§86 add_calendar_period(after, count, policy) — errata E-0029.

Календарный шаг: месяцы и годы считаются ПО ДАТАМ, и снимок §85 этому вычислению не нужен ни разу. До E-0029 та же операция жила на add_business_days, и требование календаря было условным — зависящим от policy.roll. Условность не читалась ни в норме, ни в статике, и реализации на ней молча разошлись: оракул выводил дату, движок ронял MISSING_CALENDAR при чистом lawc check. Здесь пути к календарю нет вовсе, поэтому разойтись по нему нельзя.

Из policy читаются start_count и month_end; roll обязан быть no_roll, include_end — true. Иное сочетание ОТВЕРГАЕТСЯ, а не толкуется: перенос на рабочий день вернул бы снимок через политику.

add_duration_valuefunction#

def add_duration_value(after: Typed, duration: Typed, where: str = 'add_duration') -> Typed

§84.1: физическая Duration после Instant; ответ каноничен в UTC.

def add_legal_term_value(after: Typed, count: Typed, env: Any, where: str = 'add_legal_term') -> Typed

§86.1: runtime-unit dispatch для duration-binder сахара term.

Единица выбирает одну из двух уже нормативных операций; функция не преобразует единицы и не вводит календарных умолчаний.

aggregatefunction#

def aggregate(op: str, items: list[Typed], options: dict | None = None, where: str = 'aggregate', element_type: str | None = None) -> Typed

Агрегаты §59 над материализованной коллекцией. Пустая коллекция без явной политики options.empty — EMPTY_AGGREGATE (§59); политика чужого вида — TYPE_ERROR (errata E-0195).

aggregate_element_typefunction#

def aggregate_element_type(term: dict) -> str | None

§52.1/§59 (errata E-0195): вид элемента коллекции по ОБЪЯВЛЕННОМУ типу проекции comprehension.

Это единственный носитель вида, который не зависит от того, пуста коллекция или нет: ComprehensionTerm.variable объявлен схемой обязательным, и его type.name стоит в узле независимо от числа решений generator-а. Отсюда исполнение знает вид элемента ПУСТОЙ коллекции и вправе сверить с ним вид политики.

None — вида нет: вход не comprehension (достижимо рукописным CLIR) либо у проекции не объявлен тип. Молчание здесь есть «утверждать нечего», а не «совместимо со всем».

binaryfunction#

def binary(op: str, left: Typed, right: Typed) -> Typed

BinaryTerm-операции §58 (+ - * / mod) и сравнения §57 (lt/le/gt/ge/eq/ne).

callfunction#

def call(function: str, args: list[Typed], where: str = 'call', env: Any = None, call_stack: tuple[str, ...] = ()) -> Typed

CallTerm: std-функции §50/§58/§86 над вычисленными аргументами.

env — окружение evaluation (calendar.TermEnv): нужно только тем функциям, у которых §86 берёт часть входа из контекста. Чистые функции (§50 round, §58 min/max/abs) его игнорируют.

convert_valuefunction#

def convert_value(value: Typed, num: Typed, den: Typed, unit: Typed, where: str = 'convert') -> Typed

§49 (errata E-0072): перевод единицы ЯВНОЙ формой — точный фактор парой целых.

Рамка 1 DECISION-0068: неявной конверсии не существует, и этот терм — ровно объявленная ею явная форма (образцы — div_round E-0036 и exchange-snapshot §50). Реестра факторов НЕТ намеренно: фактор точен и вечен (18/5 — определение, не курс) и живёт аргументами при норме-владельце; реестр понадобится не раньше валютоподобных факторов с датой (замер phys.zadachnik, audit §3.2). Сверить тег цели с фактором не с чем — пришивка остаётся авторской и сторожится тестами акта; названо там же.

Вид результата — ПО ЗНАЧЕНИЮ, как у пары Decimal/Decimal §58: 10-гладкое частное — Quantity целевого тега, иначе точный Rational ЧИСЛОМ значения в целевой единице. Второй случай снимает границу 10-гладкости эталонной записи: перевод «1 км/ч = 5/18 м/с» до терма был невыразим вовсе — Decimal * Rational есть TYPE_ERROR (§48, вектор T163), и не-10-гладкое число не пришивалось.

Money отвергается ПОИМЁННО: конверсия валют — территория §50 (exchange rate + snapshot), и молчаливое соседство двух конверсий с разными гарантиями было бы дырой того же класса, что закрыла E-0068.

days_between_valuefunction#

def days_between_value(start: Typed, end: Typed, where: str = 'days_between') -> Typed

§84.4 (errata E-0015): число календарных дней от start до end.

Отрицательное при обратном порядке — это измерение, а не расстояние: «просрочка отрицательна» и «платёж раньше срока» суть одно и то же, и прятать знак значило бы решать за норму.

Возвращает счёт (Integer), а не носитель единицы: дневная ставка имеет размерность 1/день, а обратных единиц в v1 нет (§49 — умножение только на скаляр). Единица видна в имени функции.

Календарь §85 НЕ нужен: календарные дни считаются независимо от рабочего календаря. Рабочие дни — add_business_days §86, и подменять одно другим нельзя (у них разные ответы на одном и том же интервале).

div_round_valuefunction#

def div_round_value(dividend: Typed, divisor: Typed, precision: Typed, mode: Typed, where: str = 'div_round') -> Typed

§50/§58 (errata E-0036): частное, округлённое ЯВНОЙ политикой.

§58 обещает у Money / Decimal и Quantity / Decimal, что при неточном делении округление законно «только явной policy §50/§18», — но применить её было не к чему: round берёт уже вычисленное значение, а вычислить частное / не даёт (INEXACT_DIVISION). У пары Decimal / Decimal дыра зеркальна: / возвращает Rational (E-0005/E-0020), а round §50 определён на Decimal/Money/Quantity и Rational не берёт.

Поэтому деление и округление здесь — ОДНА операция над ТОЧНЫМ частным, а не композиция двух: композиция невыразима с обеих сторон.

Округление применяется ВСЕГДА, а не только при неточном частном. Иначе точность результата зависела бы от значения, а не от нормы, — ровно тот дефект, который E-0020 нашла у Decimal / Decimal, где от значения зависел ВИД. На представимом частном ответ совпадает с /, если объявленная точность его вмещает.

eval_data_conditionfunction#

def eval_data_condition(formula: dict, where: str, env: Any, variables: dict[str, Typed]) -> bool

Значение чистого data-условия §56 под связанными переменными.

eval_termfunction#

def eval_term(term: dict, where: str = 'term', env: Any = None, variables: dict[str, Typed] | None = None, call_stack: tuple[str, ...] = (), branch_path: list[str] | None = None) -> Typed

Вычисление чистого CLIR data-терма.

Поддерживает Literal/Var/Binary/Call/If/Let/Match. FunctionDecl не имеет особой семантики: его body рекурсивно проходит этот же evaluator.

env — окружение evaluation (§85 снапшот + §86 policy) для функций, чей вход не исчерпывается аргументами; без него календарные термы отказывают, а не подставляют умолчание (§5.1 no hidden defaults).

from_literal_termfunction#

def from_literal_term(term: dict, where: str = 'term') -> Typed

LiteralTerm (kind value) → Typed. Тип определяется TypeRef.name + unit/currency.

hours_between_valuefunction#

def hours_between_value(start: Typed, end: Typed, where: str = 'hours_between') -> Typed

§84.1 (errata E-0028): число ПОЛНЫХ часов от start до end.

Асимметрия с §84.2, ради которой это отдельная errata, а не расширение E-0026. Календарный месяц ИЗМЕРИТЬ нельзя: «сколько месяцев между 31 января и 1 марта» не имеет ответа без конвенции округления, и потому E-0026 добавила только ШАГ. Час — Duration §84.1, физическая длительность в секундах; между двумя мгновениями она однозначна, и измерение определено без всякой политики. Поэтому здесь добавляется ИЗМЕРЕНИЕ, зеркально days_between (E-0015), а шага по часам нет.

Аргументы — Instant, а не Date: §2.15 запрещает смешивать их без timezone/boundary policy, а «не ранее чем через сорок восемь часов» (Конституция РК ст. 60.7) считается от МГНОВЕНИЯ, а не от даты.

Часовой пояс не нужен и не спрашивается: разность двух мгновений от него не зависит. Политика §86 для неоднозначного local time относится к ПОСТРОЕНИЮ мгновения из локального времени — это забота дела, а не терма.

Отрицательное при обратном порядке — по той же причине, что у days_between: это измерение, а не расстояние, и прятать знак значило бы решать за норму. Дробная часть отбрасывается к нулю: «прошло сорок восемь часов» истинно с началом сорок девятого, а не с сорок восьмым и одной секундой.

is_data_conditionfunction#

def is_data_condition(formula: Any) -> bool

§56: условие IfTerm — ЧИСТОЕ Boolean-выражение данных.

Status-тест §65 (established(P) и прочие) сюда не входит: его значение берётся из ОПОРЫ, а не из данных, а вычислителю термов store недоступен. Различение нужно затем, чтобы выбор ветви не притворялся сделанным там, где он не сделан.

minutes_between_valuefunction#

def minutes_between_value(start: Typed, end: Typed, where: str = 'minutes_between') -> Typed

§84.1 (errata E-0058): число ПОЛНЫХ минут от start до end.

Тот же класс, что hours_between (E-0028), и то же обоснование дословно: §84.1 есть физическая длительность В СЕКУНДАХ, между двумя мгновениями она однозначна, и измерение определено без всякой политики. Асимметрия с §84.2 (календарный месяц ИЗМЕРИТЬ нельзя) здесь та же и по той же причине.

Почему часа было мало. hours_between отбрасывает дробную часть к нулю, поэтому норма с порогом МЕНЬШЕ часа им не выражается вовсе: «не позднее чем за 45 минут до опубликованного времени вылета» (ст. 3 п. 2 (a) Регламента (ЕС) № 261/2004 — условие применимости всего акта) даёт hours_between = 0 и при 45 минутах, и при одной минуте, и при нуле. Величину приходилось объявлять фактом дела с числом В ИМЕНИ предиката — ровно тот признак недоделки, который E-0015, E-0026 и E-0028 закрывали для дней, месяцев и часов.

Разбор, отказы и знак — общие с hours_between (одна процедура, не копия): Instant, а не Date; смешение мгновений со смещением и без — отказ, а не догадка о поясе; отрицательное при обратном порядке, потому что это измерение, а не расстояние.

month_bound_valuefunction#

def month_bound_value(day: Typed, end: bool, where: str = 'month_end_of') -> Typed

§84.2 (errata E-0084): первое (end=False) либо последнее число месяца даты day.

Границы, а не измерение и не шаг: у месяца нет однозначной длины в днях (§84.4 отказывает измерению в месяцах), но есть однозначные границы, и потому ни политика §86, ни снимок §85 здесь не читаются. Норма «не позднее N-го числа следующего месяца» становится add_business_days(month_end_of(event), N calendar_day) при start_count = next_day — до errata тот же срок требовал в одном деле календарной и дневной единицы, а policy.month_end обязателен у первых и запрещён у вторых (E-0029).

raw_jsonfunction#

def raw_json(raw: Any) -> str

Сырое поле литерала в тексте issue — КАНОНОМ §208, не repr хоста.

Errata E-0197 (класс E-0076): repr печатал None, True, 'abc', {'a': 1} — ни одной из этих записей в .lawir.json, который читает автор, не существует, а Rust печатал ту же величину средствами serde. Канон §208 — единственная форма, в которой значение ВИДНО в документе, и она одна у обеих реализаций.

Воронка ТОТАЛЬНА (errata E-0202): форматирование сообщения не имеет права само отказать. JSON-float и целое вне i64 сюда ДОХОДЯТ — _json_int зовёт raw_json раньше, чем документ уходит в канон, — и до E-0202 CanonError вылетал отсюда с корневым путём $ вместо пути поля, подменяя собой отказ канона движка. Такое число печатается компактным JSON без канонической проверки (то же, что Value::to_string движка); сам отказ §208 предъявляет канон документа, а не этот текст.

round_fractionfunction#

def round_fraction(value: Fraction, scale: int, mode: str) -> Decimal

Публичный псевдоним _round_fraction для §64.2 (errata E-0136).

Ячейка округления обязана считаться ТЕМ ЖЕ кодом, что round §50: второе определение режима разошлось бы с первым на точной половине, и отношение округления утверждало бы не то, что округляет норма.

round_valuefunction#

def round_value(value: Typed, precision: Typed, mode: Typed, where: str = 'round') -> Typed

§50 round(value, precision, mode): единственный законный способ получить неточный денежный результат. Округление никогда не подразумевается — политика приходит аргументами, а не дефолтом движка.

Surface-форма §50 записана именованными аргументами (round(value, precision = 2, mode = HALF_UP)); v1-парсер именованных аргументов не имеет, поэтому позиционная форма с mode-строкой — та же конвенция, что options.rounding у average §59.

§50 (errata E-0110): round над Integer — тождество: дробной части у целого нет, precision её не порождает, конверсии в Decimal §48 не возникает. precision и mode при этом проверяются так же, как у прочих видов — политика обязана быть законной, даже когда ей нечего решать. До errata здесь был TYPE_ERROR, и норма, округлявшая целочисленный параметр A3 «по аналогии» с денежным, молча не срабатывала.

select_match_armfunction#

def select_match_arm(subject: Typed, arms: list, where: str, env: Any, variables: dict[str, Typed])

§55.1: первая совпавшая arm и её связывания, либо None.

Порядок arm входит в semantic hash: перебор идёт сверху вниз и останавливается на первом совпадении. Значения ОСТАЛЬНЫХ arm не вычисляются — как и невыбранная ветвь §56.

text_length_valuefunction#

def text_length_value(text: Typed, where: str = 'text_length') -> Typed

§257 (errata E-0166): число кодовых точек (Unicode scalar values) текста после NFC §13 — Integer ≥ 0. Единица — кодовая точка, не байт и не графемный кластер: «кириллица» — 9, q + U+0303 — 2, «й» в NFC — 1. Документы канона §208 уже несут NFC, поэтому нормализации здесь нет; len строки Python и есть счёт кодовых точек. Снимок §85, политика §86 и локаль не читаются — терм чист.

to_literal_termfunction#

def to_literal_term(value: Typed) -> dict

Typed → literal-терм §53 (обратное к from_literal_term).

Имена типов — urn:law:std# §257; числовые значения канонизируются _canonical_decimal §18/§208, чтобы одно и то же значение давало одни байты.

weekday_valuefunction#

def weekday_value(day: Typed, where: str = 'weekday_of') -> Typed

§84.2 (errata E-0091): день недели даты day — Integer от 1 (понедельник) до 7 (воскресенье), нумерация ISO 8601.

Свойство самой шкалы Date (пролептический григорианский календарь, §2.15), а не снимка §85: календарь §85 знает, какие дни рабочие, но день недели у даты один и тот же при любом календаре, поэтому ни снимок, ни политика §86 не читаются. До errata «the third Monday in January» (5 U.S.C. §6103(a)) и «holiday that occurs on a Saturday» (§6103(b)(1)) не выводились: даты объявлялись фактами дела и сверялись глазами.

with_unit_valuefunction#

def with_unit_value(value: Typed, unit: Typed, where: str = 'with_unit') -> Typed

§49 (errata E-0074): пришивка «число → величина» ЯВНОЙ формой.

Закрывает вторую молчаливую дыру замера phys.zadachnik (audit §2.2, Н4): обратный ход эталонной записи — умножение числа на эталон-константу — законный скаляр §49, и эталон НЕ ТОЙ единицы пришивался молча (правило выводило путь «300 секунд» со статусом TRUE_ONLY без единой диагностики). Явная форма убирает эталоны ВЫХОДА вовсе: тег цели — литерал на месте пришивки, а не имя константы, и класс опечатки «не тот эталон» исчезает.

Проверяемость — три отказа, которых у скалярного идиома не было: величина с единицей отвергается ПОИМЁННО (перетег — только convert E-0072, повторная пришивка невозможна), Money отвергается к §50, а неточная дробь — RUNTIME_ERROR по канону INEXACT_DIVISION (округление — только явной политикой §50/§18). Опечатка в самом литерале юнита остаётся авторской и сторожится тестами акта (§267) — названо честно, вслед за заявкой audit §3.2.

Побочно снята вторая половина границы «Rational не пришивается» (первую — переводы — сняла E-0072): 10-гладкая точная дробь становится Quantity канонической десятичной записью.

Documentation for Arxo. Writings — blog.arxo.io.

Anonymous visit counts on stats.arxo.io, no cookies.